-
1
require 'journey/router'
-
1
require 'journey/gtg/builder'
-
1
require 'journey/gtg/simulator'
-
1
require 'journey/nfa/builder'
-
1
require 'journey/nfa/simulator'
-
1
module Journey
-
###
-
# The Formatter class is used for formatting URLs. For example, parameters
-
# passed to +url_for+ in rails will eventually call Formatter#generate
-
1
class Formatter
-
1
attr_reader :routes
-
-
1
def initialize routes
-
2
@routes = routes
-
2
@cache = nil
-
end
-
-
1
def generate type, name, options, recall = {}, parameterize = nil
-
4
constraints = recall.merge options
-
4
missing_keys = []
-
-
4
match_route(name, constraints) do |route|
-
4
parameterized_parts = extract_parameterized_parts route, options, recall, parameterize
-
4
next if !name && route.requirements.empty? && route.parts.empty?
-
-
4
missing_keys = missing_keys(route, parameterized_parts)
-
4
next unless missing_keys.empty?
-
4
params = options.dup.delete_if do |key, _|
-
8
parameterized_parts.key?(key) || route.defaults.key?(key)
-
end
-
-
4
return [route.format(parameterized_parts), params]
-
end
-
-
raise Router::RoutingError.new "missing required keys: #{missing_keys}"
-
end
-
-
1
def clear
-
2
@cache = nil
-
end
-
-
1
private
-
1
def extract_parameterized_parts route, options, recall, parameterize = nil
-
4
constraints = recall.merge options
-
4
data = constraints.dup
-
-
4
keys_to_keep = route.parts.reverse.drop_while { |part|
-
8
!options.key?(part) || (options[part] || recall[part]).nil?
-
} | route.required_parts
-
-
4
(data.keys - keys_to_keep).each do |bad_key|
-
4
data.delete bad_key
-
end
-
-
4
parameterized_parts = data.dup
-
-
4
if parameterize
-
4
parameterized_parts.each do |k,v|
-
4
parameterized_parts[k] = parameterize.call(k, v)
-
end
-
end
-
-
8
parameterized_parts.keep_if { |_,v| v }
-
4
parameterized_parts
-
end
-
-
1
def named_routes
-
6
routes.named_routes
-
end
-
-
1
def match_route name, options
-
4
if named_routes.key? name
-
2
yield named_routes[name]
-
else
-
#routes = possibles(@cache, options.to_a)
-
2
routes = non_recursive(cache, options.to_a)
-
-
2
hash = routes.group_by { |_, r|
-
2
r.score options
-
}
-
-
2
hash.keys.sort.reverse_each do |score|
-
2
next if score < 0
-
-
4
hash[score].sort_by { |i,_| i }.each do |_,route|
-
2
yield route
-
end
-
end
-
end
-
end
-
-
1
def non_recursive cache, options
-
2
routes = []
-
2
stack = [cache]
-
-
2
while stack.any?
-
2
c = stack.shift
-
2
routes.concat c[:___routes] if c.key? :___routes
-
-
2
options.each do |pair|
-
4
stack << c[pair] if c.key? pair
-
end
-
end
-
-
2
routes
-
end
-
-
# returns an array populated with missing keys if any are present
-
1
def missing_keys route, parts
-
4
missing_keys = []
-
4
tests = route.path.requirements
-
4
route.required_parts.each { |key|
-
2
if tests.key? key
-
2
missing_keys << key unless /\A#{tests[key]}\Z/ === parts[key]
-
else
-
missing_keys << key unless parts[key]
-
end
-
}
-
4
missing_keys
-
end
-
-
1
def possibles cache, options, depth = 0
-
cache.fetch(:___routes) { [] } + options.find_all { |pair|
-
cache.key? pair
-
}.map { |pair|
-
possibles(cache[pair], options, depth + 1)
-
}.flatten(1)
-
end
-
-
# returns boolean, true if no missing keys are present
-
1
def verify_required_parts! route, parts
-
missing_keys(route, parts).empty?
-
end
-
-
1
def build_cache
-
1
root = { :___routes => [] }
-
1
routes.each_with_index do |route, i|
-
2
leaf = route.required_defaults.inject(root) do |h, tuple|
-
2
h[tuple] ||= {}
-
end
-
2
(leaf[:___routes] ||= []) << [i, route]
-
end
-
1
root
-
end
-
-
1
def cache
-
2
@cache ||= build_cache
-
end
-
end
-
end
-
1
require 'journey/gtg/transition_table'
-
-
1
module Journey
-
1
module GTG
-
1
class Builder
-
1
DUMMY = Nodes::Dummy.new # :nodoc:
-
-
1
attr_reader :root, :ast, :endpoints
-
-
1
def initialize root
-
3
@root = root
-
3
@ast = Nodes::Cat.new root, DUMMY
-
3
@followpos = nil
-
end
-
-
1
def transition_table
-
dtrans = TransitionTable.new
-
marked = {}
-
state_id = Hash.new { |h,k| h[k] = h.length }
-
-
start = firstpos(root)
-
dstates = [start]
-
until dstates.empty?
-
s = dstates.shift
-
next if marked[s]
-
marked[s] = true # mark s
-
-
s.group_by { |state| symbol(state) }.each do |sym, ps|
-
u = ps.map { |l| followpos(l) }.flatten
-
next if u.empty?
-
-
if u.uniq == [DUMMY]
-
from = state_id[s]
-
to = state_id[Object.new]
-
dtrans[from, to] = sym
-
-
dtrans.add_accepting to
-
ps.each { |state| dtrans.add_memo to, state.memo }
-
else
-
dtrans[state_id[s], state_id[u]] = sym
-
-
if u.include? DUMMY
-
to = state_id[u]
-
-
accepting = ps.find_all { |l| followpos(l).include? DUMMY }
-
-
accepting.each { |accepting_state|
-
dtrans.add_memo to, accepting_state.memo
-
}
-
-
dtrans.add_accepting state_id[u]
-
end
-
end
-
-
dstates << u
-
end
-
end
-
-
dtrans
-
end
-
-
1
def nullable? node
-
109
case node
-
when Nodes::Group
-
11
true
-
when Nodes::Star
-
true
-
when Nodes::Or
-
node.children.any? { |c| nullable?(c) }
-
when Nodes::Cat
-
2
nullable?(node.left) && nullable?(node.right)
-
when Nodes::Terminal
-
96
!node.left
-
when Nodes::Unary
-
nullable? node.left
-
else
-
raise ArgumentError, 'unknown nullable: %s' % node.class.name
-
end
-
end
-
-
1
def firstpos node
-
55
case node
-
when Nodes::Star
-
firstpos(node.left)
-
when Nodes::Cat
-
13
if nullable? node.left
-
firstpos(node.left) | firstpos(node.right)
-
else
-
13
firstpos(node.left)
-
end
-
when Nodes::Or
-
node.children.map { |c| firstpos(c) }.flatten.uniq
-
when Nodes::Unary
-
11
firstpos(node.left)
-
when Nodes::Terminal
-
31
nullable?(node) ? [] : [node]
-
else
-
raise ArgumentError, 'unknown firstpos: %s' % node.class.name
-
end
-
end
-
-
1
def lastpos node
-
74
case node
-
when Nodes::Star
-
firstpos(node.left)
-
when Nodes::Or
-
node.children.map { |c| lastpos(c) }.flatten.uniq
-
when Nodes::Cat
-
32
if nullable? node.right
-
11
lastpos(node.left) | lastpos(node.right)
-
else
-
21
lastpos(node.right)
-
end
-
when Nodes::Terminal
-
31
nullable?(node) ? [] : [node]
-
when Nodes::Unary
-
11
lastpos(node.left)
-
else
-
raise ArgumentError, 'unknown lastpos: %s' % node.class.name
-
end
-
end
-
-
1
def followpos node
-
10
followpos_table[node]
-
end
-
-
1
private
-
1
def followpos_table
-
10
@followpos ||= build_followpos
-
end
-
-
1
def build_followpos
-
23
table = Hash.new { |h,k| h[k] = [] }
-
3
@ast.each do |n|
-
50
case n
-
when Nodes::Cat
-
20
lastpos(n.left).each do |i|
-
31
table[i] += firstpos(n.right)
-
end
-
when Nodes::Star
-
lastpos(n).each do |i|
-
table[i] += firstpos(n)
-
end
-
end
-
end
-
3
table
-
end
-
-
1
def symbol edge
-
case edge
-
when Journey::Nodes::Symbol
-
edge.regexp
-
else
-
edge.left
-
end
-
end
-
end
-
end
-
end
-
1
require 'strscan'
-
-
1
module Journey
-
1
module GTG
-
1
class MatchData
-
1
attr_reader :memos
-
-
1
def initialize memos
-
@memos = memos
-
end
-
end
-
-
1
class Simulator
-
1
attr_reader :tt
-
-
1
def initialize transition_table
-
@tt = transition_table
-
end
-
-
1
def simulate string
-
input = StringScanner.new string
-
state = [0]
-
while sym = input.scan(%r([/.?]|[^/.?]+))
-
state = tt.move(state, sym)
-
end
-
-
acceptance_states = state.find_all { |s|
-
tt.accepting? s
-
}
-
-
return if acceptance_states.empty?
-
-
memos = acceptance_states.map { |x| tt.memo x }.flatten.compact
-
-
MatchData.new memos
-
end
-
-
1
alias :=~ :simulate
-
1
alias :match :simulate
-
end
-
end
-
end
-
-
1
require 'journey/nfa/dot'
-
-
1
module Journey
-
1
module GTG
-
1
class TransitionTable
-
1
include Journey::NFA::Dot
-
-
1
attr_reader :memos
-
-
1
def initialize
-
@regexp_states = Hash.new { |h,k| h[k] = {} }
-
@string_states = Hash.new { |h,k| h[k] = {} }
-
@accepting = {}
-
@memos = Hash.new { |h,k| h[k] = [] }
-
end
-
-
1
def add_accepting state
-
@accepting[state] = true
-
end
-
-
1
def accepting_states
-
@accepting.keys
-
end
-
-
1
def accepting? state
-
@accepting[state]
-
end
-
-
1
def add_memo idx, memo
-
@memos[idx] << memo
-
end
-
-
1
def memo idx
-
@memos[idx]
-
end
-
-
1
def eclosure t
-
Array(t)
-
end
-
-
1
def move t, a
-
move_string(t, a).concat move_regexp(t, a)
-
end
-
-
1
def to_json
-
require 'json'
-
-
simple_regexp = Hash.new { |h,k| h[k] = {} }
-
-
@regexp_states.each do |from, hash|
-
hash.each do |re, to|
-
simple_regexp[from][re.source] = to
-
end
-
end
-
-
JSON.dump({
-
:regexp_states => simple_regexp,
-
:string_states => @string_states,
-
:accepting => @accepting
-
})
-
end
-
-
1
def to_svg
-
svg = IO.popen("dot -Tsvg", 'w+') { |f|
-
f.write to_dot
-
f.close_write
-
f.readlines
-
}
-
3.times { svg.shift }
-
svg.join.sub(/width="[^"]*"/, '').sub(/height="[^"]*"/, '')
-
end
-
-
1
def visualizer paths, title = 'FSM'
-
viz_dir = File.join File.dirname(__FILE__), '..', 'visualizer'
-
fsm_js = File.read File.join(viz_dir, 'fsm.js')
-
fsm_css = File.read File.join(viz_dir, 'fsm.css')
-
erb = File.read File.join(viz_dir, 'index.html.erb')
-
states = "function tt() { return #{to_json}; }"
-
-
fun_routes = paths.shuffle.first(3).map do |ast|
-
ast.map { |n|
-
case n
-
when Nodes::Symbol
-
case n.left
-
when ':id' then rand(100).to_s
-
when ':format' then %w{ xml json }.shuffle.first
-
else
-
'omg'
-
end
-
when Nodes::Terminal then n.symbol
-
else
-
nil
-
end
-
}.compact.join
-
end
-
-
stylesheets = [fsm_css]
-
svg = to_svg
-
javascripts = [states, fsm_js]
-
-
# Annoying hack for 1.9 warnings
-
fun_routes = fun_routes
-
stylesheets = stylesheets
-
svg = svg
-
javascripts = javascripts
-
-
require 'erb'
-
template = ERB.new erb
-
template.result(binding)
-
end
-
-
1
def []= from, to, sym
-
case sym
-
when String
-
@string_states[from][sym] = to
-
when Regexp
-
@regexp_states[from][sym] = to
-
else
-
raise ArgumentError, 'unknown symbol: %s' % sym.class
-
end
-
end
-
-
1
def states
-
ss = @string_states.keys + @string_states.values.map(&:values).flatten
-
rs = @regexp_states.keys + @regexp_states.values.map(&:values).flatten
-
(ss + rs).uniq
-
end
-
-
1
def transitions
-
@string_states.map { |from, hash|
-
hash.map { |s, to| [from, s, to] }
-
}.flatten(1) + @regexp_states.map { |from, hash|
-
hash.map { |s, to| [from, s, to] }
-
}.flatten(1)
-
end
-
-
1
private
-
1
def move_regexp t, a
-
return [] if t.empty?
-
-
t.map { |s|
-
@regexp_states[s].map { |re,v| re === a ? v : nil }
-
}.flatten.compact.uniq
-
end
-
-
1
def move_string t, a
-
return [] if t.empty?
-
-
t.map { |s| @string_states[s][a] }.compact
-
end
-
end
-
end
-
end
-
1
require 'journey/nfa/transition_table'
-
1
require 'journey/gtg/transition_table'
-
-
1
module Journey
-
1
module NFA
-
1
class Visitor < Visitors::Visitor
-
1
def initialize tt
-
@tt = tt
-
@i = -1
-
end
-
-
1
def visit_CAT node
-
left = visit node.left
-
right = visit node.right
-
-
@tt.merge left.last, right.first
-
-
[left.first, right.last]
-
end
-
-
1
def visit_GROUP node
-
from = @i += 1
-
left = visit node.left
-
to = @i += 1
-
-
@tt.accepting = to
-
-
@tt[from, left.first] = nil
-
@tt[left.last, to] = nil
-
@tt[from, to] = nil
-
-
[from, to]
-
end
-
-
1
def visit_OR node
-
from = @i += 1
-
children = node.children.map { |c| visit c }
-
to = @i += 1
-
-
children.each do |child|
-
@tt[from, child.first] = nil
-
@tt[child.last, to] = nil
-
end
-
-
@tt.accepting = to
-
-
[from, to]
-
end
-
-
1
def terminal node
-
from_i = @i += 1 # new state
-
to_i = @i += 1 # new state
-
-
@tt[from_i, to_i] = node
-
@tt.accepting = to_i
-
@tt.add_memo to_i, node.memo
-
-
[from_i, to_i]
-
end
-
end
-
-
1
class Builder
-
1
def initialize ast
-
@ast = ast
-
end
-
-
1
def transition_table
-
tt = TransitionTable.new
-
Visitor.new(tt).accept @ast
-
tt
-
end
-
end
-
end
-
end
-
# encoding: utf-8
-
-
1
module Journey
-
1
module NFA
-
1
module Dot
-
1
def to_dot
-
edges = transitions.map { |from, sym, to|
-
" #{from} -> #{to} [label=\"#{sym || 'ε'}\"];"
-
}
-
-
#memo_nodes = memos.values.flatten.map { |n|
-
# label = n
-
# if Journey::Route === n
-
# label = "#{n.verb.source} #{n.path.spec}"
-
# end
-
# " #{n.object_id} [label=\"#{label}\", shape=box];"
-
#}
-
#memo_edges = memos.map { |k, memos|
-
# (memos || []).map { |v| " #{k} -> #{v.object_id};" }
-
#}.flatten.uniq
-
-
<<-eodot
-
digraph nfa {
-
rankdir=LR;
-
node [shape = doublecircle];
-
#{accepting_states.join ' '};
-
node [shape = circle];
-
#{edges.join "\n"}
-
}
-
eodot
-
end
-
end
-
end
-
end
-
1
require 'strscan'
-
-
1
module Journey
-
1
module NFA
-
1
class MatchData
-
1
attr_reader :memos
-
-
1
def initialize memos
-
@memos = memos
-
end
-
end
-
-
1
class Simulator
-
1
attr_reader :tt
-
-
1
def initialize transition_table
-
@tt = transition_table
-
end
-
-
1
def simulate string
-
input = StringScanner.new string
-
state = tt.eclosure 0
-
until input.eos?
-
sym = input.scan(%r([/.?]|[^/.?]+))
-
-
# FIXME: tt.eclosure is not needed for the GTG
-
state = tt.eclosure tt.move(state, sym)
-
end
-
-
acceptance_states = state.find_all { |s|
-
tt.accepting? tt.eclosure(s).sort.last
-
}
-
-
return if acceptance_states.empty?
-
-
memos = acceptance_states.map { |x| tt.memo x }.flatten.compact
-
-
MatchData.new memos
-
end
-
-
1
alias :=~ :simulate
-
1
alias :match :simulate
-
end
-
end
-
end
-
1
require 'journey/nfa/dot'
-
-
1
module Journey
-
1
module NFA
-
1
class TransitionTable
-
1
include Journey::NFA::Dot
-
-
1
attr_accessor :accepting
-
1
attr_reader :memos
-
-
1
def initialize
-
@table = Hash.new { |h,f| h[f] = {} }
-
@memos = {}
-
@accepting = nil
-
@inverted = nil
-
end
-
-
1
def accepting? state
-
accepting == state
-
end
-
-
1
def accepting_states
-
[accepting]
-
end
-
-
1
def add_memo idx, memo
-
@memos[idx] = memo
-
end
-
-
1
def memo idx
-
@memos[idx]
-
end
-
-
1
def []= i, f, s
-
@table[f][i] = s
-
end
-
-
1
def merge left, right
-
@memos[right] = @memos.delete left
-
@table[right] = @table.delete(left)
-
end
-
-
1
def states
-
(@table.keys + @table.values.map(&:keys).flatten).uniq
-
end
-
-
###
-
# Returns a generalized transition graph with reduced states. The states
-
# are reduced like a DFA, but the table must be simulated like an NFA.
-
#
-
# Edges of the GTG are regular expressions
-
1
def generalized_table
-
gt = GTG::TransitionTable.new
-
marked = {}
-
state_id = Hash.new { |h,k| h[k] = h.length }
-
alphabet = self.alphabet
-
-
stack = [eclosure(0)]
-
-
until stack.empty?
-
state = stack.pop
-
next if marked[state] || state.empty?
-
-
marked[state] = true
-
-
alphabet.each do |alpha|
-
next_state = eclosure(following_states(state, alpha))
-
next if next_state.empty?
-
-
gt[state_id[state], state_id[next_state]] = alpha
-
stack << next_state
-
end
-
end
-
-
final_groups = state_id.keys.find_all { |s|
-
s.sort.last == accepting
-
}
-
-
final_groups.each do |states|
-
id = state_id[states]
-
-
gt.add_accepting id
-
save = states.find { |s|
-
@memos.key?(s) && eclosure(s).sort.last == accepting
-
}
-
-
gt.add_memo id, memo(save)
-
end
-
-
gt
-
end
-
-
###
-
# Returns set of NFA states to which there is a transition on ast symbol
-
# +a+ from some state +s+ in +t+.
-
1
def following_states t, a
-
Array(t).map { |s| inverted[s][a] }.flatten.uniq
-
end
-
-
###
-
# Returns set of NFA states to which there is a transition on ast symbol
-
# +a+ from some state +s+ in +t+.
-
1
def move t, a
-
Array(t).map { |s|
-
inverted[s].keys.compact.find_all { |sym|
-
sym === a
-
}.map { |sym| inverted[s][sym] }
-
}.flatten.uniq
-
end
-
-
1
def alphabet
-
inverted.values.map(&:keys).flatten.compact.uniq.sort_by { |x| x.to_s }
-
end
-
-
###
-
# Returns a set of NFA states reachable from some NFA state +s+ in set
-
# +t+ on nil-transitions alone.
-
1
def eclosure t
-
stack = Array(t)
-
seen = {}
-
children = []
-
-
until stack.empty?
-
s = stack.pop
-
next if seen[s]
-
-
seen[s] = true
-
children << s
-
-
stack.concat inverted[s][nil]
-
end
-
-
children.uniq
-
end
-
-
1
def transitions
-
@table.map { |to, hash|
-
hash.map { |from, sym| [from, sym, to] }
-
}.flatten(1)
-
end
-
-
1
private
-
1
def inverted
-
return @inverted if @inverted
-
-
@inverted = Hash.new { |h,from|
-
h[from] = Hash.new { |j,s| j[s] = [] }
-
}
-
-
@table.each { |to, hash|
-
hash.each { |from, sym|
-
if sym
-
sym = Nodes::Symbol === sym ? sym.regexp : sym.left
-
end
-
-
@inverted[from][sym] << to
-
}
-
}
-
-
@inverted
-
end
-
end
-
end
-
end
-
#
-
# DO NOT MODIFY!!!!
-
# This file is automatically generated by Racc 1.4.8
-
# from Racc grammer file "".
-
#
-
-
1
require 'racc/parser.rb'
-
-
-
1
require 'journey/parser_extras'
-
1
module Journey
-
1
class Parser < Racc::Parser
-
##### State transition tables begin ###
-
-
1
racc_action_table = [
-
17, 21, 13, 15, 14, 7, nil, 16, 8, 19,
-
13, 15, 14, 7, 23, 16, 8, 19, 13, 15,
-
14, 7, nil, 16, 8, 13, 15, 14, 7, nil,
-
16, 8, 13, 15, 14, 7, nil, 16, 8 ]
-
-
1
racc_action_check = [
-
1, 17, 1, 1, 1, 1, nil, 1, 1, 1,
-
20, 20, 20, 20, 20, 20, 20, 20, 7, 7,
-
7, 7, nil, 7, 7, 19, 19, 19, 19, nil,
-
19, 19, 0, 0, 0, 0, nil, 0, 0 ]
-
-
1
racc_action_pointer = [
-
30, 0, nil, nil, nil, nil, nil, 16, nil, nil,
-
nil, nil, nil, nil, nil, nil, nil, 1, nil, 23,
-
8, nil, nil, nil ]
-
-
1
racc_action_default = [
-
-18, -18, -2, -3, -4, -5, -6, -18, -9, -10,
-
-11, -12, -13, -14, -15, -16, -17, -18, -1, -18,
-
-18, 24, -8, -7 ]
-
-
1
racc_goto_table = [
-
18, 1, nil, nil, nil, nil, nil, nil, 20, nil,
-
nil, nil, nil, nil, nil, nil, nil, nil, 22, 18 ]
-
-
1
racc_goto_check = [
-
2, 1, nil, nil, nil, nil, nil, nil, 1, nil,
-
nil, nil, nil, nil, nil, nil, nil, nil, 2, 2 ]
-
-
1
racc_goto_pointer = [
-
nil, 1, -1, nil, nil, nil, nil, nil, nil, nil,
-
nil ]
-
-
1
racc_goto_default = [
-
nil, nil, 2, 3, 4, 5, 6, 9, 10, 11,
-
12 ]
-
-
1
racc_reduce_table = [
-
0, 0, :racc_error,
-
2, 11, :_reduce_1,
-
1, 11, :_reduce_2,
-
1, 11, :_reduce_none,
-
1, 12, :_reduce_none,
-
1, 12, :_reduce_none,
-
1, 12, :_reduce_none,
-
3, 15, :_reduce_7,
-
3, 13, :_reduce_8,
-
1, 16, :_reduce_9,
-
1, 14, :_reduce_none,
-
1, 14, :_reduce_none,
-
1, 14, :_reduce_none,
-
1, 14, :_reduce_none,
-
1, 19, :_reduce_14,
-
1, 17, :_reduce_15,
-
1, 18, :_reduce_16,
-
1, 20, :_reduce_17 ]
-
-
1
racc_reduce_n = 18
-
-
1
racc_shift_n = 24
-
-
1
racc_token_table = {
-
false => 0,
-
:error => 1,
-
:SLASH => 2,
-
:LITERAL => 3,
-
:SYMBOL => 4,
-
:LPAREN => 5,
-
:RPAREN => 6,
-
:DOT => 7,
-
:STAR => 8,
-
:OR => 9 }
-
-
1
racc_nt_base = 10
-
-
1
racc_use_result_var = true
-
-
1
Racc_arg = [
-
racc_action_table,
-
racc_action_check,
-
racc_action_default,
-
racc_action_pointer,
-
racc_goto_table,
-
racc_goto_check,
-
racc_goto_default,
-
racc_goto_pointer,
-
racc_nt_base,
-
racc_reduce_table,
-
racc_token_table,
-
racc_shift_n,
-
racc_reduce_n,
-
racc_use_result_var ]
-
-
1
Racc_token_to_s_table = [
-
"$end",
-
"error",
-
"SLASH",
-
"LITERAL",
-
"SYMBOL",
-
"LPAREN",
-
"RPAREN",
-
"DOT",
-
"STAR",
-
"OR",
-
"$start",
-
"expressions",
-
"expression",
-
"or",
-
"terminal",
-
"group",
-
"star",
-
"symbol",
-
"literal",
-
"slash",
-
"dot" ]
-
-
1
Racc_debug_parser = false
-
-
##### State transition tables end #####
-
-
# reduce 0 omitted
-
-
1
def _reduce_1(val, _values, result)
-
31
result = Cat.new(val.first, val.last)
-
31
result
-
end
-
-
1
def _reduce_2(val, _values, result)
-
18
result = val.first
-
18
result
-
end
-
-
# reduce 3 omitted
-
-
# reduce 4 omitted
-
-
# reduce 5 omitted
-
-
# reduce 6 omitted
-
-
1
def _reduce_7(val, _values, result)
-
13
result = Group.new(val[1])
-
13
result
-
end
-
-
1
def _reduce_8(val, _values, result)
-
result = Or.new([val.first, val.last])
-
result
-
end
-
-
1
def _reduce_9(val, _values, result)
-
result = Star.new(Symbol.new(val.last))
-
result
-
end
-
-
# reduce 10 omitted
-
-
# reduce 11 omitted
-
-
# reduce 12 omitted
-
-
# reduce 13 omitted
-
-
1
def _reduce_14(val, _values, result)
-
13
result = Slash.new('/')
-
13
result
-
end
-
-
1
def _reduce_15(val, _values, result)
-
17
result = Symbol.new(val.first)
-
17
result
-
end
-
-
1
def _reduce_16(val, _values, result)
-
1
result = Literal.new(val.first)
-
1
result
-
end
-
-
1
def _reduce_17(val, _values, result)
-
5
result = Dot.new(val.first)
-
5
result
-
end
-
-
1
def _reduce_none(val, _values, result)
-
val[0]
-
end
-
-
end # class Parser
-
end # module Journey
-
1
require 'journey/scanner'
-
1
require 'journey/nodes/node'
-
-
1
module Journey
-
1
class Parser < Racc::Parser
-
1
include Journey::Nodes
-
-
1
def initialize
-
5
@scanner = Scanner.new
-
end
-
-
1
def parse string
-
5
@scanner.scan_setup string
-
5
do_parse
-
end
-
-
1
def next_token
-
67
@scanner.next_token
-
end
-
end
-
end
-
1
module Journey
-
1
class Route
-
1
attr_reader :app, :path, :verb, :defaults, :ip, :name
-
-
1
attr_reader :constraints
-
1
alias :conditions :constraints
-
-
1
attr_accessor :precedence
-
-
##
-
# +path+ is a path constraint.
-
# +constraints+ is a hash of constraints to be applied to this route.
-
1
def initialize name, app, path, constraints, defaults = {}
-
4
constraints = constraints.dup
-
4
@name = name
-
4
@app = app
-
4
@path = path
-
4
@verb = constraints[:request_method] || //
-
4
@ip = constraints.delete(:ip) || //
-
-
4
@constraints = constraints
-
8
@constraints.keep_if { |_,v| Regexp === v || String === v }
-
4
@defaults = defaults
-
4
@required_defaults = nil
-
4
@required_parts = nil
-
4
@parts = nil
-
4
@decorated_ast = nil
-
4
@precedence = 0
-
end
-
-
1
def ast
-
3
return @decorated_ast if @decorated_ast
-
-
2
@decorated_ast = path.ast
-
14
@decorated_ast.grep(Nodes::Terminal).each { |n| n.memo = self }
-
2
@decorated_ast
-
end
-
-
1
def requirements # :nodoc:
-
# needed for rails `rake routes`
-
4
path.requirements.merge(@defaults).delete_if { |_,v|
-
6
/.+?/ == v
-
}
-
end
-
-
1
def segments
-
2
@path.names
-
end
-
-
1
def required_keys
-
path.required_names.map { |x| x.to_sym } + required_defaults.keys
-
end
-
-
1
def score constraints
-
2
required_keys = path.required_names
-
6
supplied_keys = constraints.map { |k,v| v && k.to_s }.compact
-
-
2
return -1 unless (required_keys - supplied_keys).empty?
-
-
2
score = (supplied_keys & path.names).length
-
2
score + (required_defaults.length * 2)
-
end
-
-
1
def parts
-
15
@parts ||= segments.map { |n| n.to_sym }
-
end
-
1
alias :segment_keys :parts
-
-
1
def format path_options
-
4
path_options.delete_if do |key, value|
-
4
value.to_s == defaults[key].to_s && !required_parts.include?(key)
-
end
-
-
4
Visitors::Formatter.new(path_options).accept(path.spec)
-
end
-
-
1
def optional_parts
-
path.optional_names.map { |n| n.to_sym }
-
end
-
-
1
def required_parts
-
13
@required_parts ||= path.required_names.map { |n| n.to_sym }
-
end
-
-
1
def required_defaults
-
@required_defaults ||= begin
-
2
matches = parts
-
4
@defaults.dup.delete_if { |k,_| matches.include? k }
-
4
end
-
end
-
end
-
end
-
1
require 'journey/router/utils'
-
1
require 'journey/router/strexp'
-
1
require 'journey/routes'
-
1
require 'journey/formatter'
-
-
1
before = $-w
-
1
$-w = false
-
1
require 'journey/parser'
-
1
$-w = before
-
-
1
require 'journey/route'
-
1
require 'journey/path/pattern'
-
-
1
module Journey
-
1
class Router
-
1
class RoutingError < ::StandardError
-
end
-
-
1
VERSION = '2.0.0'
-
-
1
class NullReq # :nodoc:
-
1
attr_reader :env
-
1
def initialize env
-
@env = env
-
end
-
-
1
def request_method
-
env['REQUEST_METHOD']
-
end
-
-
1
def path_info
-
env['PATH_INFO']
-
end
-
-
1
def ip
-
env['REMOTE_ADDR']
-
end
-
-
1
def [](k); env[k]; end
-
end
-
-
1
attr_reader :request_class, :formatter
-
1
attr_accessor :routes
-
-
1
def initialize routes, options
-
2
@options = options
-
2
@params_key = options[:parameters_key]
-
2
@request_class = options[:request_class] || NullReq
-
2
@routes = routes
-
end
-
-
1
def call env
-
1
env['PATH_INFO'] = Utils.normalize_path env['PATH_INFO']
-
-
1
find_routes(env).each do |match, parameters, route|
-
1
script_name, path_info, set_params = env.values_at('SCRIPT_NAME',
-
'PATH_INFO',
-
@params_key)
-
-
1
unless route.path.anchored
-
env['SCRIPT_NAME'] = (script_name.to_s + match.to_s).chomp('/')
-
env['PATH_INFO'] = match.post_match
-
end
-
-
1
env[@params_key] = (set_params || {}).merge parameters
-
-
1
status, headers, body = route.app.call(env)
-
-
1
if 'pass' == headers['X-Cascade']
-
env['SCRIPT_NAME'] = script_name
-
env['PATH_INFO'] = path_info
-
env[@params_key] = set_params
-
next
-
end
-
-
1
return [status, headers, body]
-
end
-
-
return [404, {'X-Cascade' => 'pass'}, ['Not Found']]
-
end
-
-
1
def recognize req
-
find_routes(req.env).each do |match, parameters, route|
-
unless route.path.anchored
-
req.env['SCRIPT_NAME'] = match.to_s
-
req.env['PATH_INFO'] = match.post_match.sub(/^([^\/])/, '/\1')
-
end
-
-
yield(route, nil, parameters)
-
end
-
end
-
-
1
def visualizer
-
tt = GTG::Builder.new(ast).transition_table
-
groups = partitioned_routes.first.map(&:ast).group_by { |a| a.to_s }
-
asts = groups.values.map { |v| v.first }
-
tt.visualizer asts
-
end
-
-
1
private
-
-
1
def partitioned_routes
-
1
routes.partitioned_routes
-
end
-
-
1
def ast
-
1
routes.ast
-
end
-
-
1
def simulator
-
routes.simulator
-
end
-
-
1
def custom_routes
-
1
partitioned_routes.last
-
end
-
-
1
def filter_routes path
-
1
return [] unless ast
-
data = simulator.match(path)
-
data ? data.memos : []
-
end
-
-
1
def find_routes env
-
1
req = request_class.new env
-
-
1
routes = filter_routes(req.path_info).concat custom_routes.find_all { |r|
-
1
r.path.match(req.path_info)
-
}
-
1
routes.concat get_routes_as_head(routes)
-
-
1
routes.sort_by!(&:precedence).select! { |r|
-
2
r.constraints.all? { |k,v| v === req.send(k) } &&
-
2
r.verb === req.request_method
-
}
-
2
routes.reject! { |r| req.ip && !(r.ip === req.ip) }
-
-
1
routes.map! { |r|
-
1
match_data = r.path.match(req.path_info)
-
5
match_names = match_data.names.map { |n| n.to_sym }
-
5
match_values = match_data.captures.map { |v| v && Utils.unescape_uri(v) }
-
5
info = Hash[match_names.zip(match_values).find_all { |_,y| y }]
-
-
1
[match_data, r.defaults.merge(info), r]
-
}
-
end
-
-
1
def get_routes_as_head(routes)
-
1
precedence = (routes.map(&:precedence).max || 0) + 1
-
1
routes = routes.select { |r|
-
1
r.verb === "GET" && !(r.verb === "HEAD")
-
}.map! { |r|
-
1
Route.new(r.name,
-
r.app,
-
r.path,
-
r.conditions.merge(:request_method => "HEAD"),
-
r.defaults).tap do |route|
-
1
route.precedence = r.precedence + precedence
-
end
-
}
-
1
routes.flatten!
-
1
routes
-
end
-
end
-
end
-
1
module Journey
-
1
class Router
-
1
class Strexp
-
1
class << self
-
1
alias :compile :new
-
end
-
-
1
attr_reader :path, :requirements, :separators, :anchor
-
-
1
def initialize path, requirements, separators, anchor = true
-
5
@path = path
-
5
@requirements = requirements
-
5
@separators = separators
-
5
@anchor = anchor
-
end
-
-
1
def names
-
@path.scan(/:\w+/).map { |s| s.tr(':', '') }
-
end
-
end
-
end
-
end
-
1
require 'uri'
-
-
1
module Journey
-
1
class Router
-
1
class Utils
-
# Normalizes URI path.
-
#
-
# Strips off trailing slash and ensures there is a leading slash.
-
#
-
# normalize_path("/foo") # => "/foo"
-
# normalize_path("/foo/") # => "/foo"
-
# normalize_path("foo") # => "/foo"
-
# normalize_path("") # => "/"
-
1
def self.normalize_path(path)
-
7
path = "/#{path}"
-
7
path.squeeze!('/')
-
7
path.sub!(%r{/+\Z}, '')
-
7
path = '/' if path == ''
-
7
path
-
end
-
-
# URI path and fragment escaping
-
# http://tools.ietf.org/html/rfc3986
-
1
module UriEscape
-
# Symbol captures can generate multiple path segments, so include /.
-
1
reserved_segment = '/'
-
1
reserved_fragment = '/?'
-
1
reserved_pchar = ':@&=+$,;%'
-
-
1
safe_pchar = "#{URI::REGEXP::PATTERN::UNRESERVED}#{reserved_pchar}"
-
1
safe_segment = "#{safe_pchar}#{reserved_segment}"
-
1
safe_fragment = "#{safe_pchar}#{reserved_fragment}"
-
1
if RUBY_VERSION >= '1.9'
-
1
UNSAFE_SEGMENT = Regexp.new("[^#{safe_segment}]", false).freeze
-
1
UNSAFE_FRAGMENT = Regexp.new("[^#{safe_fragment}]", false).freeze
-
else
-
UNSAFE_SEGMENT = Regexp.new("[^#{safe_segment}]", false, 'N').freeze
-
UNSAFE_FRAGMENT = Regexp.new("[^#{safe_fragment}]", false, 'N').freeze
-
end
-
end
-
-
1
Parser = URI.const_defined?(:Parser) ? URI::Parser.new : URI
-
-
1
def self.escape_path(path)
-
4
Parser.escape(path.to_s, UriEscape::UNSAFE_SEGMENT)
-
end
-
-
1
def self.escape_fragment(fragment)
-
Parser.escape(fragment.to_s, UriEscape::UNSAFE_FRAGMENT)
-
end
-
-
1
def self.unescape_uri(uri)
-
2
Parser.unescape(uri)
-
end
-
end
-
end
-
end
-
1
module Journey
-
###
-
# The Routing table. Contains all routes for a system. Routes can be
-
# added to the table by calling Routes#add_route
-
1
class Routes
-
1
include Enumerable
-
-
1
attr_reader :routes, :named_routes
-
-
1
def initialize
-
2
@routes = []
-
2
@named_routes = {}
-
2
@ast = nil
-
2
@partitioned_routes = nil
-
2
@simulator = nil
-
end
-
-
1
def length
-
@routes.length
-
end
-
1
alias :size :length
-
-
1
def last
-
@routes.last
-
end
-
-
1
def each(&block)
-
1
routes.each(&block)
-
end
-
-
1
def clear
-
2
routes.clear
-
end
-
-
1
def partitioned_routes
-
@partitioned_routes ||= routes.partition { |r|
-
2
r.path.anchored && r.ast.grep(Nodes::Symbol).all? { |n| n.default_regexp? }
-
2
}
-
end
-
-
1
def ast
-
1
return @ast if @ast
-
1
return if partitioned_routes.first.empty?
-
-
asts = partitioned_routes.first.map { |r| r.ast }
-
@ast = Nodes::Or.new(asts)
-
end
-
-
1
def simulator
-
return @simulator if @simulator
-
-
gtg = GTG::Builder.new(ast).transition_table
-
@simulator = GTG::Simulator.new gtg
-
end
-
-
###
-
# Add a route to the routing table.
-
1
def add_route app, path, conditions, defaults, name = nil
-
3
route = Route.new(name, app, path, conditions, defaults)
-
-
3
route.precedence = routes.length
-
3
routes << route
-
3
named_routes[name] = route if name && !named_routes[name]
-
3
clear_cache!
-
3
route
-
end
-
-
1
private
-
1
def clear_cache!
-
3
@ast = nil
-
3
@partitioned_routes = nil
-
3
@simulator = nil
-
end
-
end
-
end
-
1
require 'strscan'
-
-
1
module Journey
-
1
class Scanner
-
1
def initialize
-
5
@ss = nil
-
end
-
-
1
def scan_setup str
-
5
@ss = StringScanner.new str
-
end
-
-
1
def eos?
-
@ss.eos?
-
end
-
-
1
def pos
-
@ss.pos
-
end
-
-
1
def pre_match
-
@ss.pre_match
-
end
-
-
1
def next_token
-
67
return if @ss.eos?
-
-
62
until token = scan || @ss.eos?; end
-
62
token
-
end
-
-
1
private
-
1
def scan
-
case
-
# /
-
when text = @ss.scan(/\//)
-
13
[:SLASH, text]
-
when text = @ss.scan(/\*\w+/)
-
[:STAR, text]
-
when text = @ss.scan(/\(/)
-
13
[:LPAREN, text]
-
when text = @ss.scan(/\)/)
-
13
[:RPAREN, text]
-
when text = @ss.scan(/\|/)
-
[:OR, text]
-
when text = @ss.scan(/\./)
-
5
[:DOT, text]
-
when text = @ss.scan(/:\w+/)
-
17
[:SYMBOL, text]
-
when text = @ss.scan(/[\w%\-~]+/)
-
1
[:LITERAL, text]
-
# any char
-
when text = @ss.scan(/./)
-
[:LITERAL, text]
-
62
end
-
end
-
end
-
end
-
1
require 'mocha/class_method'
-
-
1
module Mocha
-
-
1
class AnyInstanceMethod < ClassMethod
-
-
1
def mock
-
2
stubbee.any_instance.mocha
-
end
-
-
1
def reset_mocha
-
1
stubbee.any_instance.reset_mocha
-
end
-
-
1
def hide_original_method
-
1
if method_exists?(method)
-
1
begin
-
1
@original_method = stubbee.instance_method(method)
-
1
if @original_method && @original_method.owner == stubbee
-
1
@original_visibility = :public
-
1
if stubbee.protected_instance_methods.include?(method)
-
@original_visibility = :protected
-
elsif stubbee.private_instance_methods.include?(method)
-
@original_visibility = :private
-
end
-
1
stubbee.send(:remove_method, method)
-
end
-
rescue NameError
-
# deal with nasties like ActiveRecord::Associations::AssociationProxy
-
end
-
end
-
end
-
-
1
def define_new_method
-
1
stubbee.class_eval(%{
-
def #{method}(*args, &block)
-
self.class.any_instance.mocha.method_missing(:#{method}, *args, &block)
-
end
-
}, __FILE__, __LINE__)
-
end
-
-
1
def remove_new_method
-
1
stubbee.send(:remove_method, method)
-
end
-
-
1
def restore_original_method
-
1
if @original_method && @original_method.owner == stubbee
-
1
stubbee.send(:define_method, method, @original_method)
-
1
stubbee.send(@original_visibility, method)
-
end
-
end
-
-
1
def method_exists?(method)
-
1
return true if stubbee.public_instance_methods(false).include?(method)
-
return true if stubbee.protected_instance_methods(false).include?(method)
-
return true if stubbee.private_instance_methods(false).include?(method)
-
return false
-
end
-
-
end
-
-
end
-
1
require 'mocha/parameter_matchers'
-
1
require 'mocha/hooks'
-
1
require 'mocha/mockery'
-
1
require 'mocha/sequence'
-
1
require 'mocha/object_methods'
-
1
require 'mocha/module_methods'
-
1
require 'mocha/class_methods'
-
-
1
module Mocha
-
-
# Methods added to +Test::Unit::TestCase+, +MiniTest::Unit::TestCase+ or equivalent.
-
1
module API
-
-
1
include ParameterMatchers
-
1
include Hooks
-
-
# @private
-
1
def self.included(mod)
-
1
Object.send(:include, Mocha::ObjectMethods)
-
1
Module.send(:include, Mocha::ModuleMethods)
-
1
Class.send(:include, Mocha::ClassMethods)
-
end
-
-
# Builds a new mock object
-
#
-
# @param [String] name identifies mock object in error messages.
-
# @param [Hash] expected_methods_vs_return_values expected method name symbols as keys and corresponding return values as values - these expectations are setup as if {Mock#expects} were called multiple times.
-
# @yield optional block to be evaluated against the mock object instance, giving an alternative way to setup expectations.
-
# @return [Mock] a new mock object
-
#
-
# @overload def mock(name, &block)
-
# @overload def mock(expected_methods_vs_return_values = {}, &block)
-
# @overload def mock(name, expected_methods_vs_return_values = {}, &block)
-
#
-
# @example Using expected_methods_vs_return_values Hash to setup expectations.
-
# def test_motor_starts_and_stops
-
# motor = mock('motor', :start => true, :stop => true)
-
# assert motor.start
-
# assert motor.stop
-
# # an error will be raised unless both Motor#start and Motor#stop have been called
-
# end
-
# @example Using the optional block to setup expectations & stubbed methods.
-
# def test_motor_starts_and_stops
-
# motor = mock('motor') do
-
# expects(:start).with(100.rpm).returns(true)
-
# stubs(:stop).returns(true)
-
# end
-
# assert motor.start(100.rpm)
-
# assert motor.stop
-
# # an error will only be raised if Motor#start(100.rpm) has not been called
-
# end
-
1
def mock(*arguments, &block)
-
name = arguments.shift if arguments.first.is_a?(String)
-
expectations = arguments.shift || {}
-
mock = name ? Mockery.instance.named_mock(name, &block) : Mockery.instance.unnamed_mock(&block)
-
mock.expects(expectations)
-
mock
-
end
-
-
# Builds a new mock object
-
#
-
# @param [String] name identifies mock object in error messages.
-
# @param [Hash] stubbed_methods_vs_return_values stubbed method name symbols as keys and corresponding return values as values - these stubbed methods are setup as if {Mock#stubs} were called multiple times.
-
# @yield optional block to be evaluated against the mock object instance, giving an alternative way to setup stubbed methods.
-
# @return [Mock] a new mock object
-
#
-
# @overload def stub(name, &block)
-
# @overload def stub(stubbed_methods_vs_return_values = {}, &block)
-
# @overload def stub(name, stubbed_methods_vs_return_values = {}, &block)
-
#
-
# @example Using stubbed_methods_vs_return_values Hash to setup stubbed methods.
-
# def test_motor_starts_and_stops
-
# motor = mock('motor', :start => true, :stop => true)
-
# assert motor.start
-
# assert motor.stop
-
# # an error will not be raised even if either Motor#start or Motor#stop has not been called
-
# end
-
#
-
# @example Using the optional block to setup expectations & stubbed methods.
-
# def test_motor_starts_and_stops
-
# motor = mock('motor') do
-
# expects(:start).with(100.rpm).returns(true)
-
# stubs(:stop).returns(true)
-
# end
-
# assert motor.start(100.rpm)
-
# assert motor.stop
-
# # an error will only be raised if Motor#start(100.rpm) has not been called
-
# end
-
1
def stub(*arguments, &block)
-
name = arguments.shift if arguments.first.is_a?(String)
-
expectations = arguments.shift || {}
-
stub = name ? Mockery.instance.named_mock(name, &block) : Mockery.instance.unnamed_mock(&block)
-
stub.stubs(expectations)
-
stub
-
end
-
-
# Builds a mock object that accepts calls to any method. By default it will return +nil+ for any method call.
-
#
-
# @param [String] name identifies mock object in error messages.
-
# @param [Hash] stubbed_methods_vs_return_values stubbed method name symbols as keys and corresponding return values as values - these stubbed methods are setup as if {Mock#stubs} were called multiple times.
-
# @yield optional block to be evaluated against the mock object instance, giving an alternative way to setup stubbed methods.
-
# @return [Mock] a new mock object
-
#
-
# @overload def stub_everything(name, &block)
-
# @overload def stub_everything(stubbed_methods_vs_return_values = {}, &block)
-
# @overload def stub_everything(name, stubbed_methods_vs_return_values = {}, &block)
-
#
-
# @example Ignore invocations of irrelevant methods.
-
# def test_motor_stops
-
# motor = stub_everything('motor', :stop => true)
-
# assert_nil motor.irrelevant_method_1 # => no error raised
-
# assert_nil motor.irrelevant_method_2 # => no error raised
-
# assert motor.stop
-
# end
-
1
def stub_everything(*arguments, &block)
-
name = arguments.shift if arguments.first.is_a?(String)
-
expectations = arguments.shift || {}
-
stub = name ? Mockery.instance.named_mock(name, &block) : Mockery.instance.unnamed_mock(&block)
-
stub.stub_everything
-
stub.stubs(expectations)
-
stub
-
end
-
-
# Builds a new sequence which can be used to constrain the order in which expectations can occur.
-
#
-
# Specify that an expected invocation must occur within a named {Sequence} by using {Expectation#in_sequence}.
-
#
-
# @return [Sequence] a new sequence
-
#
-
# @see Expectation#in_sequence
-
#
-
# @example Ensure methods on egg are invoked in correct order.
-
# breakfast = sequence('breakfast')
-
#
-
# egg = mock('egg') do
-
# expects(:crack).in_sequence(breakfast)
-
# expects(:fry).in_sequence(breakfast)
-
# expects(:eat).in_sequence(breakfast)
-
# end
-
1
def sequence(name)
-
Sequence.new(name)
-
end
-
-
# Builds a new state machine which can be used to constrain the order in which expectations can occur.
-
#
-
# Specify the initial state of the state machine by using {StateMachine#starts_as}.
-
#
-
# Specify that an expected invocation should change the state of the state machine by using {Expectation#then}.
-
#
-
# Specify that an expected invocation should be constrained to occur within a particular +state+ by using {Expectation#when}.
-
#
-
# A test can contain multiple state machines.
-
#
-
# @return [StateMachine] a new state machine
-
#
-
# @see Expectation#then
-
# @see Expectation#when
-
# @see StateMachine
-
# @example Constrain expected invocations to occur in particular states.
-
# power = states('power').starts_as('off')
-
#
-
# radio = mock('radio') do
-
# expects(:switch_on).then(power.is('on'))
-
# expects(:select_channel).with('BBC Radio 4').when(power.is('on'))
-
# expects(:adjust_volume).with(+5).when(power.is('on'))
-
# expects(:select_channel).with('BBC World Service').when(power.is('on'))
-
# expects(:adjust_volume).with(-5).when(power.is('on'))
-
# expects(:switch_off).then(power.is('off'))
-
# end
-
1
def states(name)
-
Mockery.instance.new_state_machine(name)
-
end
-
-
end
-
-
end
-
1
module Mocha
-
-
1
class ArgumentIterator
-
-
1
def initialize(argument)
-
28
@argument = argument
-
end
-
-
1
def each(&block)
-
28
if @argument.is_a?(Hash) then
-
@argument.each do |method_name, return_value|
-
block.call(method_name, return_value)
-
end
-
else
-
28
block.call(@argument)
-
end
-
end
-
-
end
-
-
end
-
1
module Mocha
-
-
1
class BacktraceFilter
-
-
1
LIB_DIRECTORY = File.expand_path(File.join(File.dirname(__FILE__), "..")) + File::SEPARATOR
-
-
1
def initialize(lib_directory = LIB_DIRECTORY)
-
@path_pattern = Regexp.new(lib_directory)
-
end
-
-
1
def filtered(backtrace)
-
backtrace.reject { |location| @path_pattern.match(File.expand_path(location)) }
-
end
-
-
end
-
-
end
-
1
module Mocha
-
-
1
class Cardinality
-
-
1
INFINITY = 1 / 0.0
-
-
1
class << self
-
-
1
def exactly(count)
-
18
new(count, count)
-
end
-
-
1
def at_least(count)
-
1
new(count, INFINITY)
-
end
-
-
1
def at_most(count)
-
new(0, count)
-
end
-
-
1
def times(range_or_count)
-
case range_or_count
-
when Range then new(range_or_count.first, range_or_count.last)
-
else new(range_or_count, range_or_count)
-
end
-
end
-
-
end
-
-
1
def initialize(required, maximum)
-
19
@required, @maximum = required, maximum
-
end
-
-
1
def invocations_allowed?(invocation_count)
-
14
invocation_count < maximum
-
end
-
-
1
def satisfied?(invocations_so_far)
-
invocations_so_far >= required
-
end
-
-
1
def needs_verifying?
-
!allowed_any_number_of_times?
-
end
-
-
1
def verified?(invocation_count)
-
14
(invocation_count >= required) && (invocation_count <= maximum)
-
end
-
-
1
def allowed_any_number_of_times?
-
required == 0 && infinite?(maximum)
-
end
-
-
1
def used?(invocation_count)
-
(invocation_count > 0) || (maximum == 0)
-
end
-
-
1
def mocha_inspect
-
if allowed_any_number_of_times?
-
"allowed any number of times"
-
else
-
if required == 0 && maximum == 0
-
"expected never"
-
elsif required == maximum
-
"expected exactly #{times(required)}"
-
elsif infinite?(maximum)
-
"expected at least #{times(required)}"
-
elsif required == 0
-
"expected at most #{times(maximum)}"
-
else
-
"expected between #{required} and #{times(maximum)}"
-
end
-
end
-
end
-
-
1
protected
-
-
1
attr_reader :required, :maximum
-
-
1
def times(number)
-
case number
-
when 0 then "no times"
-
when 1 then "once"
-
when 2 then "twice"
-
else "#{number} times"
-
end
-
end
-
-
1
def infinite?(number)
-
number.respond_to?(:infinite?) && number.infinite?
-
end
-
-
end
-
-
end
-
1
module Mocha
-
-
1
class Central
-
-
1
attr_accessor :stubba_methods
-
-
1
def initialize
-
146
self.stubba_methods = []
-
end
-
-
1
def stub(method)
-
17
unless stubba_methods.detect { |m| m.matches?(method) }
-
14
method.stub
-
14
stubba_methods.push(method)
-
end
-
end
-
-
1
def unstub(method)
-
28
if existing = stubba_methods.detect { |m| m.matches?(method) }
-
14
existing.unstub
-
14
stubba_methods.delete(existing)
-
end
-
end
-
-
1
def unstub_all
-
146
while stubba_methods.any? do
-
14
unstub(stubba_methods.first)
-
end
-
end
-
-
end
-
-
end
-
1
module Mocha
-
-
1
class ChangeStateSideEffect
-
-
1
def initialize(state)
-
@state = state
-
end
-
-
1
def perform
-
@state.activate
-
end
-
-
1
def mocha_inspect
-
"then #{@state.mocha_inspect}"
-
end
-
-
end
-
-
end
-
1
require 'metaclass'
-
-
1
module Mocha
-
-
1
class ClassMethod
-
-
1
attr_reader :stubbee, :method
-
-
1
def initialize(stubbee, method)
-
14
@stubbee, @original_method = stubbee, nil
-
14
@method = RUBY_VERSION < '1.9' ? method.to_s : method.to_sym
-
end
-
-
1
def stub
-
14
hide_original_method
-
14
define_new_method
-
end
-
-
1
def unstub
-
14
remove_new_method
-
14
restore_original_method
-
14
mock.unstub(method.to_sym)
-
14
unless mock.any_expectations?
-
14
reset_mocha
-
end
-
end
-
-
1
def mock
-
26
stubbee.mocha
-
end
-
-
1
def reset_mocha
-
13
stubbee.reset_mocha
-
end
-
-
1
def hide_original_method
-
13
if method_exists?(method)
-
12
begin
-
12
@original_method = stubbee._method(method)
-
12
if @original_method && @original_method.owner == stubbee.__metaclass__
-
9
@original_visibility = :public
-
9
if stubbee.__metaclass__.protected_instance_methods.include?(method)
-
@original_visibility = :protected
-
elsif stubbee.__metaclass__.private_instance_methods.include?(method)
-
@original_visibility = :private
-
end
-
9
stubbee.__metaclass__.send(:remove_method, method)
-
end
-
rescue NameError
-
# deal with nasties like ActiveRecord::Associations::AssociationProxy
-
end
-
end
-
end
-
-
1
def define_new_method
-
13
stubbee.__metaclass__.class_eval(%{
-
def #{method}(*args, &block)
-
mocha.method_missing(:#{method}, *args, &block)
-
end
-
}, __FILE__, __LINE__)
-
end
-
-
1
def remove_new_method
-
13
stubbee.__metaclass__.send(:remove_method, method)
-
end
-
-
1
def restore_original_method
-
13
if @original_method && @original_method.owner == stubbee.__metaclass__
-
9
if RUBY_VERSION < '1.9'
-
original_method = @original_method
-
stubbee.__metaclass__.send(:define_method, method) do |*args, &block|
-
original_method.call(*args, &block)
-
end
-
else
-
9
stubbee.__metaclass__.send(:define_method, method, @original_method)
-
end
-
9
stubbee.__metaclass__.send(@original_visibility, method)
-
end
-
end
-
-
1
def matches?(other)
-
17
return false unless (other.class == self.class)
-
16
(stubbee.object_id == other.stubbee.object_id) and (method == other.method)
-
end
-
-
1
alias_method :==, :eql?
-
-
1
def to_s
-
"#{stubbee}.#{method}"
-
end
-
-
1
def method_exists?(method)
-
12
symbol = method.to_sym
-
12
__metaclass__ = stubbee.__metaclass__
-
12
__metaclass__.public_method_defined?(symbol) || __metaclass__.protected_method_defined?(symbol) || __metaclass__.private_method_defined?(symbol)
-
end
-
-
end
-
-
end
-
1
require 'mocha/mockery'
-
1
require 'mocha/class_method'
-
1
require 'mocha/any_instance_method'
-
-
1
module Mocha
-
-
# Methods added to all classes to allow mocking and stubbing on real (i.e. non-mock) objects.
-
1
module ClassMethods
-
-
# @private
-
1
def stubba_method
-
12
Mocha::ClassMethod
-
end
-
-
# @private
-
1
class AnyInstance
-
-
1
def initialize(klass)
-
1
@stubba_object = klass
-
end
-
-
1
def mocha
-
3
@mocha ||= Mocha::Mockery.instance.mock_impersonating_any_instance_of(@stubba_object)
-
end
-
-
1
def stubba_method
-
1
Mocha::AnyInstanceMethod
-
end
-
-
1
def stubba_object
-
1
@stubba_object
-
end
-
-
1
def method_exists?(method, include_public_methods = true)
-
if include_public_methods
-
return true if @stubba_object.public_instance_methods(include_superclass_methods = true).include?(method)
-
end
-
return true if @stubba_object.protected_instance_methods(include_superclass_methods = true).include?(method)
-
return true if @stubba_object.private_instance_methods(include_superclass_methods = true).include?(method)
-
return false
-
end
-
-
end
-
-
# @return [Mock] a mock object which will detect calls to any instance of this class.
-
# @raise [StubbingError] if attempting to stub method which is not allowed.
-
#
-
# @example Return false to invocation of +Product#save+ for any instance of +Product+.
-
# Product.any_instance.stubs(:save).returns(false)
-
# product_1 = Product.new
-
# assert_equal false, product_1.save
-
# product_2 = Product.new
-
# assert_equal false, product_2.save
-
1
def any_instance
-
4
if frozen?
-
raise StubbingError.new("can't stub method on frozen object: #{mocha_inspect}.any_instance", caller)
-
end
-
4
@any_instance ||= AnyInstance.new(self)
-
end
-
-
end
-
-
end
-
1
module Mocha
-
-
# Configuration settings.
-
1
class Configuration
-
-
1
DEFAULTS = {
-
:stubbing_method_unnecessarily => :allow,
-
:stubbing_method_on_non_mock_object => :allow,
-
:stubbing_non_existent_method => :allow,
-
:stubbing_non_public_method => :allow,
-
:stubbing_method_on_nil => :prevent,
-
}
-
-
1
class << self
-
-
# Allow the specified +action+.
-
#
-
# @param [Symbol] action one of +:stubbing_method_unnecessarily+, +:stubbing_method_on_non_mock_object+, +:stubbing_non_existent_method+, +:stubbing_non_public_method+, +:stubbing_method_on_nil+.
-
# @yield optional block during which the configuration change will be changed before being returned to its original value at the end of the block.
-
1
def allow(action, &block)
-
change_config action, :allow, &block
-
end
-
-
# @private
-
1
def allow?(action)
-
70
configuration[action] == :allow
-
end
-
-
# Warn if the specified +action+ is attempted.
-
#
-
# @param [Symbol] action one of +:stubbing_method_unnecessarily+, +:stubbing_method_on_non_mock_object+, +:stubbing_non_existent_method+, +:stubbing_non_public_method+, +:stubbing_method_on_nil+.
-
# @yield optional block during which the configuration change will be changed before being returned to its original value at the end of the block.
-
1
def warn_when(action, &block)
-
change_config action, :warn, &block
-
end
-
-
# @private
-
1
def warn_when?(action)
-
configuration[action] == :warn
-
end
-
-
# Raise a {StubbingError} if if the specified +action+ is attempted.
-
#
-
# @param [Symbol] action one of +:stubbing_method_unnecessarily+, +:stubbing_method_on_non_mock_object+, +:stubbing_non_existent_method+, +:stubbing_non_public_method+, +:stubbing_method_on_nil+.
-
# @yield optional block during which the configuration change will be changed before being returned to its original value at the end of the block.
-
1
def prevent(action, &block)
-
change_config action, :prevent, &block
-
end
-
-
# @private
-
1
def prevent?(action)
-
configuration[action] == :prevent
-
end
-
-
# @private
-
1
def reset_configuration
-
@configuration = nil
-
end
-
-
1
private
-
-
# @private
-
1
def configuration
-
70
@configuration ||= DEFAULTS.dup
-
end
-
-
# @private
-
1
def change_config(action, new_value, &block)
-
if block_given?
-
temporarily_change_config action, new_value, &block
-
else
-
configuration[action] = new_value
-
end
-
end
-
-
# @private
-
1
def temporarily_change_config(action, new_value, &block)
-
original_value = configuration[action]
-
configuration[action] = new_value
-
yield
-
ensure
-
configuration[action] = original_value
-
end
-
-
end
-
-
end
-
-
end
-
1
module Mocha
-
-
1
class ExceptionRaiser
-
-
1
def initialize(exception, message)
-
@exception, @message = exception, message
-
end
-
-
1
def evaluate
-
raise @exception, @exception.to_s if @exception.is_a?(Module) && (@exception < Interrupt)
-
raise @exception, @message if @message
-
raise @exception
-
end
-
-
end
-
-
end
-
1
require 'mocha/method_matcher'
-
1
require 'mocha/parameters_matcher'
-
1
require 'mocha/expectation_error'
-
1
require 'mocha/return_values'
-
1
require 'mocha/exception_raiser'
-
1
require 'mocha/thrower'
-
1
require 'mocha/yield_parameters'
-
1
require 'mocha/is_a'
-
1
require 'mocha/in_state_ordering_constraint'
-
1
require 'mocha/change_state_side_effect'
-
1
require 'mocha/cardinality'
-
-
1
module Mocha
-
-
# Methods on expectations returned from {Mock#expects}, {Mock#stubs}, {ObjectMethods#expects} and {ObjectMethods#stubs}.
-
1
class Expectation
-
-
# Modifies expectation so that the number of calls to the expected method must be within a specific +range+.
-
#
-
# @param [Range,Integer] range specifies the allowable range in the number of expected invocations.
-
# @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained.
-
#
-
# @example Specifying a specific number of expected invocations.
-
# object = mock()
-
# object.expects(:expected_method).times(3)
-
# 3.times { object.expected_method }
-
# # => verify succeeds
-
#
-
# object = mock()
-
# object.expects(:expected_method).times(3)
-
# 2.times { object.expected_method }
-
# # => verify fails
-
#
-
# @example Specifying a range in the number of expected invocations.
-
# object = mock()
-
# object.expects(:expected_method).times(2..4)
-
# 3.times { object.expected_method }
-
# # => verify succeeds
-
#
-
# object = mock()
-
# object.expects(:expected_method).times(2..4)
-
# object.expected_method
-
# # => verify fails
-
1
def times(range)
-
@cardinality = Cardinality.times(range)
-
self
-
end
-
-
# Modifies expectation so that the expected method must be called exactly twice.
-
#
-
# @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained.
-
#
-
# @example Expected method must be invoked exactly twice.
-
# object = mock()
-
# object.expects(:expected_method).twice
-
# object.expected_method
-
# object.expected_method
-
# # => verify succeeds
-
#
-
# object = mock()
-
# object.expects(:expected_method).twice
-
# object.expected_method
-
# object.expected_method
-
# object.expected_method # => unexpected invocation
-
#
-
# object = mock()
-
# object.expects(:expected_method).twice
-
# object.expected_method
-
# # => verify fails
-
1
def twice
-
@cardinality = Cardinality.exactly(2)
-
self
-
end
-
-
# Modifies expectation so that the expected method must be called exactly once.
-
#
-
# Note that this is the default behaviour for an expectation, but you may wish to use it for clarity/emphasis.
-
#
-
# @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained.
-
#
-
# @example Expected method must be invoked exactly once.
-
# object = mock()
-
# object.expects(:expected_method).once
-
# object.expected_method
-
# # => verify succeeds
-
#
-
# object = mock()
-
# object.expects(:expected_method).once
-
# object.expected_method
-
# object.expected_method # => unexpected invocation
-
#
-
# object = mock()
-
# object.expects(:expected_method).once
-
# # => verify fails
-
1
def once
-
3
@cardinality = Cardinality.exactly(1)
-
3
self
-
end
-
-
# Modifies expectation so that the expected method must never be called.
-
#
-
# @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained.
-
#
-
# @example Expected method must never be called.
-
# object = mock()
-
# object.expects(:expected_method).never
-
# object.expected_method # => unexpected invocation
-
#
-
# object = mock()
-
# object.expects(:expected_method).never
-
# # => verify succeeds
-
1
def never
-
1
@cardinality = Cardinality.exactly(0)
-
1
self
-
end
-
-
# Modifies expectation so that the expected method must be called at least a +minimum_number_of_times+.
-
#
-
# @param [Integer] minimum_number_of_times minimum number of expected invocations.
-
# @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained.
-
#
-
# @example Expected method must be called at least twice.
-
# object = mock()
-
# object.expects(:expected_method).at_least(2)
-
# 3.times { object.expected_method }
-
# # => verify succeeds
-
#
-
# object = mock()
-
# object.expects(:expected_method).at_least(2)
-
# object.expected_method
-
# # => verify fails
-
1
def at_least(minimum_number_of_times)
-
1
@cardinality = Cardinality.at_least(minimum_number_of_times)
-
1
self
-
end
-
-
# Modifies expectation so that the expected method must be called at least once.
-
#
-
# @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained.
-
#
-
# @example Expected method must be called at least once.
-
# object = mock()
-
# object.expects(:expected_method).at_least_once
-
# object.expected_method
-
# # => verify succeeds
-
#
-
# object = mock()
-
# object.expects(:expected_method).at_least_once
-
# # => verify fails
-
1
def at_least_once
-
at_least(1)
-
self
-
end
-
-
# Modifies expectation so that the expected method must be called at most a +maximum_number_of_times+.
-
#
-
# @param [Integer] maximum_number_of_times maximum number of expected invocations.
-
# @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained.
-
#
-
# @example Expected method must be called at most twice.
-
# object = mock()
-
# object.expects(:expected_method).at_most(2)
-
# 2.times { object.expected_method }
-
# # => verify succeeds
-
#
-
# object = mock()
-
# object.expects(:expected_method).at_most(2)
-
# 3.times { object.expected_method } # => unexpected invocation
-
1
def at_most(maximum_number_of_times)
-
@cardinality = Cardinality.at_most(maximum_number_of_times)
-
self
-
end
-
-
# Modifies expectation so that the expected method must be called at most once.
-
#
-
# @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained.
-
#
-
# @example Expected method must be called at most once.
-
# object = mock()
-
# object.expects(:expected_method).at_most_once
-
# object.expected_method
-
# # => verify succeeds
-
#
-
# object = mock()
-
# object.expects(:expected_method).at_most_once
-
# 2.times { object.expected_method } # => unexpected invocation
-
1
def at_most_once()
-
at_most(1)
-
self
-
end
-
-
# Modifies expectation so that the expected method must be called with +expected_parameters+.
-
#
-
# May be used with parameter matchers in {ParameterMatchers}.
-
#
-
# @param [*Array] expected_parameters parameters expected.
-
# @yield optional block specifying custom matching.
-
# @yieldparam [*Array] actual_parameters parameters with which expected method was invoked.
-
# @yieldreturn [Boolean] +true+ if +actual_parameters+ are acceptable.
-
# @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained.
-
#
-
# @example Expected method must be called with expected parameters.
-
# object = mock()
-
# object.expects(:expected_method).with(:param1, :param2)
-
# object.expected_method(:param1, :param2)
-
# # => verify succeeds
-
#
-
# object = mock()
-
# object.expects(:expected_method).with(:param1, :param2)
-
# object.expected_method(:param3)
-
# # => verify fails
-
#
-
# @example Expected method must be called with a value divisible by 4.
-
# object = mock()
-
# object.expects(:expected_method).with() { |value| value % 4 == 0 }
-
# object.expected_method(16)
-
# # => verify succeeds
-
#
-
# object = mock()
-
# object.expects(:expected_method).with() { |value| value % 4 == 0 }
-
# object.expected_method(17)
-
# # => verify fails
-
1
def with(*expected_parameters, &matching_block)
-
8
@parameters_matcher = ParametersMatcher.new(expected_parameters, &matching_block)
-
8
self
-
end
-
-
# Modifies expectation so that when the expected method is called, it yields with the specified +parameters+.
-
#
-
# May be called multiple times on the same expectation for consecutive invocations.
-
#
-
# @param [*Array] parameters parameters to be yielded.
-
# @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained.
-
# @see #then
-
#
-
# @example Yield parameters when expected method is invoked.
-
# object = mock()
-
# object.expects(:expected_method).yields('result')
-
# yielded_value = nil
-
# object.expected_method { |value| yielded_value = value }
-
# yielded_value # => 'result'
-
#
-
# @example Yield different parameters on different invocations of the expected method.
-
# object = mock()
-
# object.stubs(:expected_method).yields(1).then.yields(2)
-
# yielded_values_from_first_invocation = []
-
# yielded_values_from_second_invocation = []
-
# object.expected_method { |value| yielded_values_from_first_invocation << value } # first invocation
-
# object.expected_method { |value| yielded_values_from_second_invocation << value } # second invocation
-
# yielded_values_from_first_invocation # => [1]
-
# yielded_values_from_second_invocation # => [2]
-
1
def yields(*parameters)
-
@yield_parameters.add(*parameters)
-
self
-
end
-
-
# Modifies expectation so that when the expected method is called, it yields multiple times per invocation with the specified +parameter_groups+.
-
#
-
# @param [*Array<Array>] parameter_groups each element of +parameter_groups+ should iself be an +Array+ representing the parameters to be passed to the block for a single yield.
-
# @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained.
-
# @see #then
-
#
-
# @example When the +expected_method+ is called, the stub will invoke the block twice, the first time it passes +'result_1'+, +'result_2'+ as the parameters, and the second time it passes 'result_3' as the parameters.
-
# object = mock()
-
# object.expects(:expected_method).multiple_yields(['result_1', 'result_2'], ['result_3'])
-
# yielded_values = []
-
# object.expected_method { |*values| yielded_values << values }
-
# yielded_values # => [['result_1', 'result_2'], ['result_3]]
-
#
-
# @example Yield different groups of parameters on different invocations of the expected method.
-
# object = mock()
-
# object.stubs(:expected_method).multiple_yields([1, 2], [3]).then.multiple_yields([4], [5, 6])
-
# yielded_values_from_first_invocation = []
-
# yielded_values_from_second_invocation = []
-
# object.expected_method { |*values| yielded_values_from_first_invocation << values } # first invocation
-
# object.expected_method { |*values| yielded_values_from_second_invocation << values } # second invocation
-
# yielded_values_from_first_invocation # => [[1, 2], [3]]
-
# yielded_values_from_second_invocation # => [[4], [5, 6]]
-
1
def multiple_yields(*parameter_groups)
-
@yield_parameters.multiple_add(*parameter_groups)
-
self
-
end
-
-
# Modifies expectation so that when the expected method is called, it returns the specified +value+.
-
#
-
# @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained.
-
# @see #then
-
#
-
# @overload def returns(value)
-
# @param [Object] value value to return on invocation of expected method.
-
# @overload def returns(*values)
-
# @param [*Array] values values to return on consecutive invocations of expected method.
-
#
-
# @example Return the same value on every invocation.
-
# object = mock()
-
# object.stubs(:stubbed_method).returns('result')
-
# object.stubbed_method # => 'result'
-
# object.stubbed_method # => 'result'
-
#
-
# @example Return a different value on consecutive invocations.
-
# object = mock()
-
# object.stubs(:stubbed_method).returns(1, 2)
-
# object.stubbed_method # => 1
-
# object.stubbed_method # => 2
-
#
-
# @example Alternative way to return a different value on consecutive invocations.
-
# object = mock()
-
# object.stubs(:expected_method).returns(1, 2).then.returns(3)
-
# object.expected_method # => 1
-
# object.expected_method # => 2
-
# object.expected_method # => 3
-
#
-
# @example May be called in conjunction with {#raises} on the same expectation.
-
# object = mock()
-
# object.stubs(:expected_method).returns(1, 2).then.raises(Exception)
-
# object.expected_method # => 1
-
# object.expected_method # => 2
-
# object.expected_method # => raises exception of class Exception1
-
#
-
# @example Note that in Ruby a method returning multiple values is exactly equivalent to a method returning an +Array+ of those values.
-
# object = mock()
-
# object.stubs(:expected_method).returns([1, 2])
-
# x, y = object.expected_method
-
# x # => 1
-
# y # => 2
-
1
def returns(*values)
-
2
@return_values += ReturnValues.build(*values)
-
2
self
-
end
-
-
# Modifies expectation so that when the expected method is called, it raises the specified +exception+ with the specified +message+ i.e. calls +Kernel#raise(exception, message)+.
-
#
-
# @param [Class,Exception,String,#exception] exception exception to be raised or message to be passed to RuntimeError.
-
# @param [String] message exception message.
-
# @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained.
-
#
-
# @see Kernel#raise
-
# @see #then
-
#
-
# @overload def raises
-
# @overload def raises(exception)
-
# @overload def raises(exception, message)
-
#
-
# @example Raise specified exception if expected method is invoked.
-
# object = stub()
-
# object.stubs(:expected_method).raises(Exception, 'message')
-
# object.expected_method # => raises exception of class Exception and with message 'message'
-
#
-
# @example Raise custom exception with extra constructor parameters by passing in an instance of the exception.
-
# object = stub()
-
# object.stubs(:expected_method).raises(MyException.new('message', 1, 2, 3))
-
# object.expected_method # => raises the specified instance of MyException
-
#
-
# @example Raise different exceptions on consecutive invocations of the expected method.
-
# object = stub()
-
# object.stubs(:expected_method).raises(Exception1).then.raises(Exception2)
-
# object.expected_method # => raises exception of class Exception1
-
# object.expected_method # => raises exception of class Exception2
-
#
-
# @example Raise an exception on first invocation of expected method and then return values on subsequent invocations.
-
# object = stub()
-
# object.stubs(:expected_method).raises(Exception).then.returns(2, 3)
-
# object.expected_method # => raises exception of class Exception1
-
# object.expected_method # => 2
-
# object.expected_method # => 3
-
1
def raises(exception = RuntimeError, message = nil)
-
@return_values += ReturnValues.new(ExceptionRaiser.new(exception, message))
-
self
-
end
-
-
# Modifies expectation so that when the expected method is called, it throws the specified +tag+ with the specific return value +object+ i.e. calls +Kernel#throw(tag, object)+.
-
#
-
# @param [Symbol,String] tag tag to throw to transfer control to the active catch block.
-
# @param [Object] object return value for the catch block.
-
# @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained.
-
#
-
# @see Kernel#throw
-
# @see #then
-
#
-
# @overload def throw(tag)
-
# @overload def throw(tag, object)
-
#
-
# @example Throw tag when expected method is invoked.
-
# object = stub()
-
# object.stubs(:expected_method).throws(:done)
-
# object.expected_method # => throws tag :done
-
#
-
# @example Throw tag with return value +object+ c.f. +Kernel#throw+.
-
# object = stub()
-
# object.stubs(:expected_method).throws(:done, 'result')
-
# object.expected_method # => throws tag :done and causes catch block to return 'result'
-
#
-
# @example Throw different tags on consecutive invocations of the expected method.
-
# object = stub()
-
# object.stubs(:expected_method).throws(:done).then.throws(:continue)
-
# object.expected_method # => throws :done
-
# object.expected_method # => throws :continue
-
#
-
# @example Throw tag on first invocation of expected method and then return values for subsequent invocations.
-
# object = stub()
-
# object.stubs(:expected_method).throws(:done).then.returns(2, 3)
-
# object.expected_method # => throws :done
-
# object.expected_method # => 2
-
# object.expected_method # => 3
-
1
def throws(tag, object = nil)
-
@return_values += ReturnValues.new(Thrower.new(tag, object))
-
self
-
end
-
-
# @overload def then
-
# Used as syntactic sugar to improve readability. It has no effect on state of the expectation.
-
# @overload def then(state_machine.is(state_name))
-
# Used to change the +state_machine+ to the state specified by +state_name+ when the expected invocation occurs.
-
# @param [StateMachine::State] state_machine.is(state_name) provides a mechanism to change the +state_machine+ into the state specified by +state_name+ when the expected method is invoked.
-
#
-
# @see API#states
-
# @see StateMachine
-
# @see #when
-
#
-
# @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained.
-
#
-
# @example Using {#then} as syntactic sugar when specifying values to be returned and exceptions to be raised on consecutive invocations of the expected method.
-
# object = mock()
-
# object.stubs(:expected_method).returns(1, 2).then.raises(Exception).then.returns(4)
-
# object.expected_method # => 1
-
# object.expected_method # => 2
-
# object.expected_method # => raises exception of class Exception
-
# object.expected_method # => 4
-
#
-
# @example Using {#then} to change the +state+ of a +state_machine+ on the invocation of an expected method.
-
# power = states('power').starts_as('off')
-
#
-
# radio = mock('radio')
-
# radio.expects(:switch_on).then(power.is('on'))
-
# radio.expects(:select_channel).with('BBC Radio 4').when(power.is('on'))
-
# radio.expects(:adjust_volume).with(+5).when(power.is('on'))
-
# radio.expects(:select_channel).with('BBC World Service').when(power.is('on'))
-
# radio.expects(:adjust_volume).with(-5).when(power.is('on'))
-
# radio.expects(:switch_off).then(power.is('off'))
-
1
def then(*parameters)
-
if parameters.length == 1
-
state = parameters.first
-
add_side_effect(ChangeStateSideEffect.new(state))
-
end
-
self
-
end
-
-
# Constrains the expectation to occur only when the +state_machine+ is in the state specified by +state_name+.
-
#
-
# @param [StateMachine::StatePredicate] state_machine.is(state_name) provides a mechanism to determine whether the +state_machine+ is in the state specified by +state_name+ when the expected method is invoked.
-
# @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained.
-
#
-
# @see API#states
-
# @see StateMachine
-
# @see #then
-
#
-
# @example Using {#when} to only allow invocation of methods when "power" state machine is in the "on" state.
-
# power = states('power').starts_as('off')
-
#
-
# radio = mock('radio')
-
# radio.expects(:switch_on).then(power.is('on'))
-
# radio.expects(:select_channel).with('BBC Radio 4').when(power.is('on'))
-
# radio.expects(:adjust_volume).with(+5).when(power.is('on'))
-
# radio.expects(:select_channel).with('BBC World Service').when(power.is('on'))
-
# radio.expects(:adjust_volume).with(-5).when(power.is('on'))
-
# radio.expects(:switch_off).then(power.is('off'))
-
1
def when(state_predicate)
-
add_ordering_constraint(InStateOrderingConstraint.new(state_predicate))
-
self
-
end
-
-
# Constrains the expectation so that it must be invoked at the current point in the +sequence+.
-
#
-
# To expect a sequence of invocations, write the expectations in order and add the +in_sequence(sequence)+ clause to each one.
-
#
-
# Expectations in a +sequence+ can have any invocation count.
-
#
-
# If an expectation in a sequence is stubbed, rather than expected, it can be skipped in the +sequence+.
-
#
-
# An expected method can appear in multiple sequences.
-
#
-
# @param [*Array<Sequence>] sequences sequences in which expected method should appear.
-
# @return [Expectation] the same expectation, thereby allowing invocations of other {Expectation} methods to be chained.
-
#
-
# @see API#sequence
-
#
-
# @example Ensure methods are invoked in a specified order.
-
# breakfast = sequence('breakfast')
-
#
-
# egg = mock('egg')
-
# egg.expects(:crack).in_sequence(breakfast)
-
# egg.expects(:fry).in_sequence(breakfast)
-
# egg.expects(:eat).in_sequence(breakfast)
-
1
def in_sequence(*sequences)
-
sequences.each { |sequence| add_in_sequence_ordering_constraint(sequence) }
-
self
-
end
-
-
# @private
-
1
attr_reader :backtrace
-
-
# @private
-
1
def initialize(mock, expected_method_name, backtrace = nil)
-
14
@mock = mock
-
14
@method_matcher = MethodMatcher.new(expected_method_name.to_sym)
-
14
@parameters_matcher = ParametersMatcher.new
-
14
@ordering_constraints = []
-
14
@side_effects = []
-
14
@cardinality, @invocation_count = Cardinality.exactly(1), 0
-
14
@return_values = ReturnValues.new
-
14
@yield_parameters = YieldParameters.new
-
14
@backtrace = backtrace || caller
-
end
-
-
# @private
-
1
def add_ordering_constraint(ordering_constraint)
-
@ordering_constraints << ordering_constraint
-
end
-
-
# @private
-
1
def add_in_sequence_ordering_constraint(sequence)
-
sequence.constrain_as_next_in_sequence(self)
-
end
-
-
# @private
-
1
def add_side_effect(side_effect)
-
@side_effects << side_effect
-
end
-
-
# @private
-
1
def perform_side_effects
-
14
@side_effects.each { |side_effect| side_effect.perform }
-
end
-
-
# @private
-
1
def in_correct_order?
-
14
@ordering_constraints.all? { |ordering_constraint| ordering_constraint.allows_invocation_now? }
-
end
-
-
# @private
-
1
def matches_method?(method_name)
-
14
@method_matcher.match?(method_name)
-
end
-
-
# @private
-
1
def match?(actual_method_name, *actual_parameters)
-
14
@method_matcher.match?(actual_method_name) && @parameters_matcher.match?(actual_parameters) && in_correct_order?
-
end
-
-
# @private
-
1
def invocations_allowed?
-
14
@cardinality.invocations_allowed?(@invocation_count)
-
end
-
-
# @private
-
1
def satisfied?
-
@cardinality.satisfied?(@invocation_count)
-
end
-
-
# @private
-
1
def invoke
-
14
@invocation_count += 1
-
14
perform_side_effects()
-
14
if block_given? then
-
2
@yield_parameters.next_invocation.each do |yield_parameters|
-
yield(*yield_parameters)
-
end
-
end
-
14
@return_values.next
-
end
-
-
# @private
-
1
def verified?(assertion_counter = nil)
-
14
assertion_counter.increment if assertion_counter && @cardinality.needs_verifying?
-
14
@cardinality.verified?(@invocation_count)
-
end
-
-
# @private
-
1
def used?
-
@cardinality.used?(@invocation_count)
-
end
-
-
# @private
-
1
def mocha_inspect
-
message = "#{@cardinality.mocha_inspect}, "
-
message << case @invocation_count
-
when 0 then "not yet invoked"
-
when 1 then "invoked once"
-
when 2 then "invoked twice"
-
else "invoked #{@invocation_count} times"
-
end
-
message << ": "
-
message << method_signature
-
message << "; #{@ordering_constraints.map { |oc| oc.mocha_inspect }.join("; ")}" unless @ordering_constraints.empty?
-
message
-
end
-
-
# @private
-
1
def method_signature
-
"#{@mock.mocha_inspect}.#{@method_matcher.mocha_inspect}#{@parameters_matcher.mocha_inspect}"
-
end
-
-
end
-
-
end
-
1
module Mocha
-
# Default exception class raised when an unexpected invocation or an unsatisfied expectation occurs.
-
#
-
# Authors of test libraries may use +Mocha::ExpectationErrorFactory+ to have Mocha raise a different exception.
-
#
-
# @see Mocha::ExpectationErrorFactory
-
1
class ExpectationError < Exception; end
-
end
-
1
require 'mocha/backtrace_filter'
-
1
require 'mocha/expectation_error'
-
-
1
module Mocha
-
-
# This factory determines what class of exception should be raised when Mocha detects a test failure.
-
#
-
# This class should only be used by authors of test libraries and not by typical "users" of Mocha.
-
#
-
# For example, it is used by +Mocha::Integration::MiniTest::Adapter+ in order to have Mocha raise a +MiniTest::Assertion+ which can then be sensibly handled by +MiniTest::Unit::TestCase+.
-
#
-
# @see Mocha::Integration::MiniTest::Adapter
-
1
class ExpectationErrorFactory
-
1
class << self
-
# @!attribute exception_class
-
# Determines what class of exception should be raised when Mocha detects a test failure.
-
#
-
# This attribute may be set by authors of test libraries in order to have Mocha raise exceptions of a specific class when there is an unexpected invocation or an unsatisfied expectation.
-
#
-
# By default a +Mocha::ExpectationError+ will be raised.
-
#
-
# @return [Exception] class of exception to be raised when an expectation error occurs
-
# @see Mocha::ExpectationError
-
1
attr_accessor :exception_class
-
-
# @private
-
1
def build(message = nil, backtrace = [])
-
self.exception_class ||= ExpectationError
-
exception = exception_class.new(message)
-
filter = BacktraceFilter.new
-
exception.set_backtrace(filter.filtered(backtrace))
-
exception
-
end
-
end
-
end
-
end
-
1
module Mocha
-
-
1
class ExpectationList
-
-
1
def initialize
-
14
@expectations = []
-
end
-
-
1
def add(expectation)
-
14
@expectations.unshift(expectation)
-
14
expectation
-
end
-
-
1
def remove_all_matching_method(method_name)
-
28
@expectations.reject! { |expectation| expectation.matches_method?(method_name) }
-
end
-
-
1
def matches_method?(method_name)
-
@expectations.any? { |expectation| expectation.matches_method?(method_name) }
-
end
-
-
1
def match(method_name, *arguments)
-
matching_expectations(method_name, *arguments).first
-
end
-
-
1
def match_allowing_invocation(method_name, *arguments)
-
28
matching_expectations(method_name, *arguments).detect { |e| e.invocations_allowed? }
-
end
-
-
1
def verified?(assertion_counter = nil)
-
28
@expectations.all? { |expectation| expectation.verified?(assertion_counter) }
-
end
-
-
1
def to_a
-
14
@expectations
-
end
-
-
1
def to_set
-
@expectations.to_set
-
end
-
-
1
def length
-
@expectations.length
-
end
-
-
1
def any?
-
14
@expectations.any?
-
end
-
-
1
private
-
-
1
def matching_expectations(method_name, *arguments)
-
28
@expectations.select { |e| e.match?(method_name, *arguments) }
-
end
-
-
end
-
-
end
-
1
require 'mocha/mockery'
-
-
1
module Mocha
-
-
# Integration hooks for test library authors.
-
#
-
# The methods in this module should be called from test libraries wishing to integrate with Mocha.
-
#
-
# This module is provided as part of the +Mocha::API+ module and is therefore part of the public API, but should only be used by authors of test libraries and not by typical "users" of Mocha.
-
#
-
# Integration with Test::Unit and MiniTest are provided as part of Mocha, because they are (or were once) part of the Ruby standard library. Integration with other test libraries is not provided as *part* of Mocha, but is supported by means of the methods in this module.
-
#
-
# See the code in the +Adapter+ modules for examples of how to use the methods in this module. +Mocha::ExpectationErrorFactory+ may be used if you want +Mocha+ to raise a different type of exception.
-
#
-
# @see Mocha::Integration::TestUnit::Adapter
-
# @see Mocha::Integration::MiniTest::Adapter
-
# @see Mocha::ExpectationErrorFactory
-
# @see Mocha::API
-
1
module Hooks
-
# Prepares Mocha before a test (only for use by authors of test libraries).
-
#
-
# This method should be called before each individual test starts (including before any "setup" code).
-
1
def mocha_setup
-
end
-
-
# Verifies that all mock expectations have been met (only for use by authors of test libraries).
-
#
-
# This is equivalent to a series of "assertions".
-
#
-
# This method should be called at the end of each individual test, before it has been determined whether or not the test has passed.
-
1
def mocha_verify(assertion_counter = nil)
-
146
Mockery.instance.verify(assertion_counter)
-
end
-
-
# Resets Mocha after a test (only for use by authors of test libraries).
-
#
-
# This method should be called after each individual test has finished (including after any "teardown" code).
-
1
def mocha_teardown
-
146
Mockery.instance.teardown
-
146
Mockery.reset_instance
-
end
-
end
-
end
-
1
module Mocha
-
-
1
class InStateOrderingConstraint
-
-
1
def initialize(state_predicate)
-
@state_predicate = state_predicate
-
end
-
-
1
def allows_invocation_now?
-
@state_predicate.active?
-
end
-
-
1
def mocha_inspect
-
"when #{@state_predicate.mocha_inspect}"
-
end
-
-
end
-
-
end
-
1
require 'date'
-
-
1
module Mocha
-
-
1
module ObjectMethods
-
1
def mocha_inspect
-
address = self.__id__ * 2
-
address += 0x100000000 if address < 0
-
inspect =~ /#</ ? "#<#{self.class}:0x#{'%x' % address}>" : inspect
-
end
-
end
-
-
1
module StringMethods
-
1
def mocha_inspect
-
inspect.gsub(/\"/, "'")
-
end
-
end
-
-
1
module ArrayMethods
-
1
def mocha_inspect
-
"[#{collect { |member| member.mocha_inspect }.join(', ')}]"
-
end
-
end
-
-
1
module HashMethods
-
1
def mocha_inspect
-
"{#{collect { |key, value| "#{key.mocha_inspect} => #{value.mocha_inspect}" }.join(', ')}}"
-
end
-
end
-
-
1
module TimeMethods
-
1
def mocha_inspect
-
"#{inspect} (#{to_f} secs)"
-
end
-
end
-
-
1
module DateMethods
-
1
def mocha_inspect
-
to_s
-
end
-
end
-
-
end
-
-
1
class Object
-
1
include Mocha::ObjectMethods
-
end
-
-
1
class String
-
1
include Mocha::StringMethods
-
end
-
-
1
class Array
-
1
include Mocha::ArrayMethods
-
end
-
-
1
class Hash
-
1
include Mocha::HashMethods
-
end
-
-
1
class Time
-
1
include Mocha::TimeMethods
-
end
-
-
1
class Date
-
1
include Mocha::DateMethods
-
end
-
1
require 'mocha/class_method'
-
-
1
module Mocha
-
-
1
class InstanceMethod < ClassMethod
-
-
1
def method_exists?(method)
-
1
return true if stubbee.public_methods(false).include?(method)
-
1
return true if stubbee.protected_methods(false).include?(method)
-
1
return true if stubbee.private_methods(false).include?(method)
-
return false
-
end
-
-
end
-
-
end
-
1
class Object
-
-
# :stopdoc:
-
-
1
alias_method :__is_a__, :is_a?
-
-
# :startdoc:
-
-
end
-
1
module Mocha
-
-
1
class Logger
-
-
1
def initialize(io)
-
@io = io
-
end
-
-
1
def warn(message)
-
@io.puts "WARNING: #{message}"
-
end
-
-
end
-
-
end
-
1
module Mocha
-
-
1
class MethodMatcher
-
-
1
attr_reader :expected_method_name
-
-
1
def initialize(expected_method_name)
-
14
@expected_method_name = expected_method_name
-
end
-
-
1
def match?(actual_method_name)
-
28
@expected_method_name == actual_method_name.to_sym
-
end
-
-
1
def mocha_inspect
-
"#{@expected_method_name}"
-
end
-
-
end
-
-
end
-
1
require 'metaclass'
-
1
require 'mocha/expectation'
-
1
require 'mocha/expectation_list'
-
1
require 'mocha/names'
-
1
require 'mocha/method_matcher'
-
1
require 'mocha/parameters_matcher'
-
1
require 'mocha/unexpected_invocation'
-
1
require 'mocha/argument_iterator'
-
1
require 'mocha/expectation_error_factory'
-
-
1
module Mocha
-
-
# Traditional mock object.
-
#
-
# All methods return an {Expectation} which can be further modified by methods on {Expectation}.
-
1
class Mock
-
-
# Adds an expectation that the specified method must be called exactly once with any parameters.
-
#
-
# @param [Symbol,String] method_name name of expected method
-
# @param [Hash] expected_methods_vs_return_values expected method name symbols as keys and corresponding return values as values - these expectations are setup as if {#expects} were called multiple times.
-
#
-
# @overload def expects(method_name)
-
# @overload def expects(expected_methods_vs_return_values)
-
# @return [Expectation] last-built expectation which can be further modified by methods on {Expectation}.
-
#
-
# @example Expected method invoked once so no error raised
-
# object = mock()
-
# object.expects(:expected_method)
-
# object.expected_method
-
#
-
# @example Expected method not invoked so error raised
-
# object = mock()
-
# object.expects(:expected_method)
-
# # error raised when test completes, because expected_method not called exactly once
-
#
-
# @example Expected method invoked twice so error raised
-
# object = mock()
-
# object.expects(:expected_method)
-
# object.expected_method
-
# object.expected_method # => error raised when expected method invoked second time
-
#
-
# @example Setup multiple expectations using +expected_methods_vs_return_values+.
-
# object = mock()
-
# object.expects(:expected_method_one => :result_one, :expected_method_two => :result_two)
-
#
-
# # is exactly equivalent to
-
#
-
# object = mock()
-
# object.expects(:expected_method_one).returns(:result_one)
-
# object.expects(:expected_method_two).returns(:result_two)
-
1
def expects(method_name_or_hash, backtrace = nil)
-
13
iterator = ArgumentIterator.new(method_name_or_hash)
-
13
iterator.each { |*args|
-
13
method_name = args.shift
-
13
ensure_method_not_already_defined(method_name)
-
13
expectation = Expectation.new(self, method_name, backtrace)
-
13
expectation.returns(args.shift) if args.length > 0
-
13
@expectations.add(expectation)
-
}
-
end
-
-
# Adds an expectation that the specified method may be called any number of times with any parameters.
-
#
-
# @param [Symbol,String] method_name name of stubbed method
-
# @param [Hash] stubbed_methods_vs_return_values stubbed method name symbols as keys and corresponding return values as values - these stubbed methods are setup as if {#stubs} were called multiple times.
-
#
-
# @overload def stubs(method_name)
-
# @overload def stubs(stubbed_methods_vs_return_values)
-
# @return [Expectation] last-built expectation which can be further modified by methods on {Expectation}.
-
#
-
# @example No error raised however many times stubbed method is invoked
-
# object = mock()
-
# object.stubs(:stubbed_method)
-
# object.stubbed_method
-
# object.stubbed_method
-
# # no error raised
-
#
-
# @example Setup multiple expectations using +stubbed_methods_vs_return_values+.
-
# object = mock()
-
# object.stubs(:stubbed_method_one => :result_one, :stubbed_method_two => :result_two)
-
#
-
# # is exactly equivalent to
-
#
-
# object = mock()
-
# object.stubs(:stubbed_method_one).returns(:result_one)
-
# object.stubs(:stubbed_method_two).returns(:result_two)
-
1
def stubs(method_name_or_hash, backtrace = nil)
-
1
iterator = ArgumentIterator.new(method_name_or_hash)
-
1
iterator.each { |*args|
-
1
method_name = args.shift
-
1
ensure_method_not_already_defined(method_name)
-
1
expectation = Expectation.new(self, method_name, backtrace)
-
1
expectation.at_least(0)
-
1
expectation.returns(args.shift) if args.length > 0
-
1
@expectations.add(expectation)
-
}
-
end
-
-
# Removes the specified stubbed method (added by calls to {#expects} or {#stubs}) and all expectations associated with it.
-
#
-
# @param [Symbol] method_name name of method to unstub.
-
#
-
# @example Invoking an unstubbed method causes error to be raised
-
# object = mock('mock') do
-
# object.stubs(:stubbed_method).returns(:result_one)
-
# object.stubbed_method # => :result_one
-
# object.unstub(:stubbed_method)
-
# object.stubbed_method # => unexpected invocation: #<Mock:mock>.stubbed_method()
-
1
def unstub(method_name)
-
14
@expectations.remove_all_matching_method(method_name)
-
end
-
-
# Constrains the {Mock} instance so that it can only expect or stub methods to which +responder+ responds. The constraint is only applied at method invocation time.
-
#
-
# A +NoMethodError+ will be raised if the +responder+ does not +#respond_to?+ a method invocation (even if the method has been expected or stubbed).
-
#
-
# The {Mock} instance will delegate its +#respond_to?+ method to the +responder+.
-
#
-
# @param [Object, #respond_to?] responder an object used to determine whether {Mock} instance should +#respond_to?+ to an invocation.
-
# @return [Mock] the same {Mock} instance, thereby allowing invocations of other {Mock} methods to be chained.
-
#
-
# @example Normal mocking
-
# sheep = mock('sheep')
-
# sheep.expects(:chew)
-
# sheep.expects(:foo)
-
# sheep.respond_to?(:chew) # => true
-
# sheep.respond_to?(:foo) # => true
-
# sheep.chew
-
# sheep.foo
-
# # no error raised
-
#
-
# @example Using {#responds_like} with an instance method
-
# class Sheep
-
# def chew(grass); end
-
# end
-
#
-
# sheep = mock('sheep')
-
# sheep.responds_like(Sheep.new)
-
# sheep.expects(:chew)
-
# sheep.expects(:foo)
-
# sheep.respond_to?(:chew) # => true
-
# sheep.respond_to?(:foo) # => false
-
# sheep.chew
-
# sheep.foo # => raises NoMethodError exception
-
#
-
# @example Using {#responds_like} with a class method
-
# class Sheep
-
# def self.number_of_legs; end
-
# end
-
#
-
# sheep_class = mock('sheep_class')
-
# sheep_class.responds_like(Sheep)
-
# sheep_class.stubs(:number_of_legs).returns(4)
-
# sheep_class.expects(:foo)
-
# sheep_class.respond_to?(:number_of_legs) # => true
-
# sheep_class.respond_to?(:foo) # => false
-
# assert_equal 4, sheep_class.number_of_legs
-
# sheep_class.foo # => raises NoMethodError exception
-
1
def responds_like(responder)
-
@responder = responder
-
self
-
end
-
-
# @private
-
1
def initialize(mockery, name = nil, &block)
-
14
@mockery = mockery
-
14
@name = name || DefaultName.new(self)
-
14
@expectations = ExpectationList.new
-
14
@everything_stubbed = false
-
14
@responder = nil
-
14
instance_eval(&block) if block
-
end
-
-
# @private
-
1
attr_reader :everything_stubbed
-
-
1
alias_method :__expects__, :expects
-
-
1
alias_method :__stubs__, :stubs
-
-
1
alias_method :quacks_like, :responds_like
-
-
# @private
-
1
def __expectations__
-
14
@expectations
-
end
-
-
# @private
-
1
def stub_everything
-
@everything_stubbed = true
-
end
-
-
# @private
-
1
def method_missing(symbol, *arguments, &block)
-
14
if @responder and not @responder.respond_to?(symbol)
-
raise NoMethodError, "undefined method `#{symbol}' for #{self.mocha_inspect} which responds like #{@responder.mocha_inspect}"
-
end
-
14
if matching_expectation_allowing_invocation = @expectations.match_allowing_invocation(symbol, *arguments)
-
14
matching_expectation_allowing_invocation.invoke(&block)
-
else
-
if (matching_expectation = @expectations.match(symbol, *arguments)) || (!matching_expectation && !@everything_stubbed)
-
matching_expectation.invoke(&block) if matching_expectation
-
message = UnexpectedInvocation.new(self, symbol, *arguments).to_s
-
message << @mockery.mocha_inspect
-
raise ExpectationErrorFactory.build(message, caller)
-
end
-
end
-
end
-
-
# @private
-
1
def respond_to?(symbol, include_private = false)
-
if @responder then
-
if @responder.method(:respond_to?).arity > 1
-
@responder.respond_to?(symbol, include_private)
-
else
-
@responder.respond_to?(symbol)
-
end
-
else
-
@everything_stubbed || @expectations.matches_method?(symbol)
-
end
-
end
-
-
# @private
-
1
def __verified__?(assertion_counter = nil)
-
14
@expectations.verified?(assertion_counter)
-
end
-
-
# @private
-
1
def mocha_inspect
-
@name.mocha_inspect
-
end
-
-
# @private
-
1
def inspect
-
mocha_inspect
-
end
-
-
# @private
-
1
def ensure_method_not_already_defined(method_name)
-
14
self.__metaclass__.send(:undef_method, method_name) if self.__metaclass__.method_defined?(method_name)
-
end
-
-
# @private
-
1
def any_expectations?
-
14
@expectations.any?
-
end
-
-
end
-
-
end
-
1
require 'mocha/central'
-
1
require 'mocha/mock'
-
1
require 'mocha/names'
-
1
require 'mocha/state_machine'
-
1
require 'mocha/logger'
-
1
require 'mocha/configuration'
-
1
require 'mocha/stubbing_error'
-
1
require 'mocha/expectation_error_factory'
-
-
1
module Mocha
-
-
1
class Mockery
-
-
1
class << self
-
-
1
def instance
-
320
@instance ||= new
-
end
-
-
1
def reset_instance
-
146
@instance = nil
-
end
-
-
end
-
-
1
def named_mock(name, &block)
-
add_mock(Mock.new(self, Name.new(name), &block))
-
end
-
-
1
def unnamed_mock(&block)
-
add_mock(Mock.new(self, &block))
-
end
-
-
1
def mock_impersonating(object, &block)
-
13
add_mock(Mock.new(self, ImpersonatingName.new(object), &block))
-
end
-
-
1
def mock_impersonating_any_instance_of(klass, &block)
-
1
add_mock(Mock.new(self, ImpersonatingAnyInstanceName.new(klass), &block))
-
end
-
-
1
def new_state_machine(name)
-
add_state_machine(StateMachine.new(name))
-
end
-
-
1
def verify(assertion_counter = nil)
-
160
unless mocks.all? { |mock| mock.__verified__?(assertion_counter) }
-
message = "not all expectations were satisfied\n#{mocha_inspect}"
-
if unsatisfied_expectations.empty?
-
backtrace = caller
-
else
-
backtrace = unsatisfied_expectations[0].backtrace
-
end
-
raise ExpectationErrorFactory.build(message, backtrace)
-
end
-
146
expectations.each do |e|
-
14
unless Mocha::Configuration.allow?(:stubbing_method_unnecessarily)
-
unless e.used?
-
on_stubbing_method_unnecessarily(e)
-
end
-
end
-
end
-
end
-
-
1
def teardown
-
146
stubba.unstub_all
-
146
reset
-
end
-
-
1
def stubba
-
160
@stubba ||= Central.new
-
end
-
-
1
def mocks
-
306
@mocks ||= []
-
end
-
-
1
def state_machines
-
@state_machines ||= []
-
end
-
-
1
def mocha_inspect
-
message = ""
-
message << "unsatisfied expectations:\n- #{unsatisfied_expectations.map { |e| e.mocha_inspect }.join("\n- ")}\n" unless unsatisfied_expectations.empty?
-
message << "satisfied expectations:\n- #{satisfied_expectations.map { |e| e.mocha_inspect }.join("\n- ")}\n" unless satisfied_expectations.empty?
-
message << "states:\n- #{state_machines.map { |sm| sm.mocha_inspect }.join("\n- ")}" unless state_machines.empty?
-
message
-
end
-
-
1
def on_stubbing(object, method)
-
14
method = RUBY_VERSION < '1.9' ? method.to_s : method.to_sym
-
14
unless Mocha::Configuration.allow?(:stubbing_non_existent_method)
-
unless object.method_exists?(method, include_public_methods = true)
-
on_stubbing_non_existent_method(object, method)
-
end
-
end
-
14
unless Mocha::Configuration.allow?(:stubbing_non_public_method)
-
if object.method_exists?(method, include_public_methods = false)
-
on_stubbing_non_public_method(object, method)
-
end
-
end
-
14
unless Mocha::Configuration.allow?(:stubbing_method_on_nil)
-
14
if object.nil?
-
on_stubbing_method_on_nil(object, method)
-
end
-
end
-
14
unless Mocha::Configuration.allow?(:stubbing_method_on_non_mock_object)
-
on_stubbing_method_on_non_mock_object(object, method)
-
end
-
end
-
-
1
def on_stubbing_non_existent_method(object, method)
-
if Mocha::Configuration.prevent?(:stubbing_non_existent_method)
-
raise StubbingError.new("stubbing non-existent method: #{object.mocha_inspect}.#{method}", caller)
-
end
-
if Mocha::Configuration.warn_when?(:stubbing_non_existent_method)
-
logger.warn "stubbing non-existent method: #{object.mocha_inspect}.#{method}"
-
end
-
end
-
-
1
def on_stubbing_non_public_method(object, method)
-
if Mocha::Configuration.prevent?(:stubbing_non_public_method)
-
raise StubbingError.new("stubbing non-public method: #{object.mocha_inspect}.#{method}", caller)
-
end
-
if Mocha::Configuration.warn_when?(:stubbing_non_public_method)
-
logger.warn "stubbing non-public method: #{object.mocha_inspect}.#{method}"
-
end
-
end
-
-
1
def on_stubbing_method_on_nil(object, method)
-
if Mocha::Configuration.prevent?(:stubbing_method_on_nil)
-
raise StubbingError.new("stubbing method on nil: #{object.mocha_inspect}.#{method}", caller)
-
end
-
if Mocha::Configuration.warn_when?(:stubbing_method_on_nil)
-
logger.warn "stubbing method on nil: #{object.mocha_inspect}.#{method}"
-
end
-
end
-
-
1
def on_stubbing_method_on_non_mock_object(object, method)
-
if Mocha::Configuration.prevent?(:stubbing_method_on_non_mock_object)
-
raise StubbingError.new("stubbing method on non-mock object: #{object.mocha_inspect}.#{method}", caller)
-
end
-
if Mocha::Configuration.warn_when?(:stubbing_method_on_non_mock_object)
-
logger.warn "stubbing method on non-mock object: #{object.mocha_inspect}.#{method}"
-
end
-
end
-
-
1
def on_stubbing_method_unnecessarily(expectation)
-
if Mocha::Configuration.prevent?(:stubbing_method_unnecessarily)
-
raise StubbingError.new("stubbing method unnecessarily: #{expectation.method_signature}", expectation.backtrace)
-
end
-
if Mocha::Configuration.warn_when?(:stubbing_method_unnecessarily)
-
logger.warn "stubbing method unnecessarily: #{expectation.method_signature}"
-
end
-
end
-
-
1
attr_writer :logger
-
-
1
def logger
-
@logger ||= Logger.new($stderr)
-
end
-
-
-
1
private
-
-
1
def expectations
-
160
mocks.map { |mock| mock.__expectations__.to_a }.flatten
-
end
-
-
1
def unsatisfied_expectations
-
expectations.reject { |e| e.verified? }
-
end
-
-
1
def satisfied_expectations
-
expectations.select { |e| e.verified? }
-
end
-
-
1
def add_mock(mock)
-
14
mocks << mock
-
14
mock
-
end
-
-
1
def add_state_machine(state_machine)
-
state_machines << state_machine
-
state_machine
-
end
-
-
1
def reset
-
146
@stubba = nil
-
146
@mocks = nil
-
146
@state_machines = nil
-
end
-
-
end
-
-
end
-
1
require 'mocha/class_method'
-
-
1
module Mocha
-
-
1
class ModuleMethod < ClassMethod
-
-
1
def method_exists?(method)
-
return true if stubbee.public_methods(false).include?(method)
-
return true if stubbee.protected_methods(false).include?(method)
-
return true if stubbee.private_methods(false).include?(method)
-
return false
-
end
-
-
end
-
-
end
-
1
require 'mocha/module_method'
-
-
1
module Mocha
-
-
# @private
-
1
module ModuleMethods
-
-
1
def stubba_method
-
Mocha::ModuleMethod
-
end
-
-
end
-
-
end
-
1
module Mocha
-
-
1
class MultipleYields
-
-
1
attr_reader :parameter_groups
-
-
1
def initialize(*parameter_groups)
-
@parameter_groups = parameter_groups
-
end
-
-
1
def each
-
@parameter_groups.each do |parameter_group|
-
yield(parameter_group)
-
end
-
end
-
-
end
-
-
end
-
-
1
module Mocha
-
-
1
class ImpersonatingName
-
-
1
def initialize(object)
-
13
@object = object
-
end
-
-
1
def mocha_inspect
-
@object.mocha_inspect
-
end
-
-
end
-
-
1
class ImpersonatingAnyInstanceName
-
-
1
def initialize(klass)
-
1
@klass = klass
-
end
-
-
1
def mocha_inspect
-
"#<AnyInstance:#{@klass.mocha_inspect}>"
-
end
-
-
end
-
-
1
class Name
-
-
1
def initialize(name)
-
@name = name
-
end
-
-
1
def mocha_inspect
-
"#<Mock:#{@name}>"
-
end
-
-
end
-
-
1
class DefaultName
-
-
1
def initialize(mock)
-
@mock = mock
-
end
-
-
1
def mocha_inspect
-
address = @mock.__id__ * 2
-
address += 0x100000000 if address < 0
-
"#<Mock:0x#{'%x' % address}>"
-
end
-
-
end
-
-
end
-
1
module Mocha
-
-
1
class NoYields
-
-
1
def each
-
end
-
-
end
-
-
end
-
-
1
require 'mocha/mockery'
-
1
require 'mocha/instance_method'
-
1
require 'mocha/argument_iterator'
-
1
require 'mocha/expectation_error_factory'
-
-
1
module Mocha
-
-
# Methods added to all objects to allow mocking and stubbing on real (i.e. non-mock) objects.
-
#
-
# Both {#expects} and {#stubs} return an {Expectation} which can be further modified by methods on {Expectation}.
-
1
module ObjectMethods
-
-
# @private
-
1
alias_method :_method, :method
-
-
# @private
-
1
def mocha
-
53
@mocha ||= Mocha::Mockery.instance.mock_impersonating(self)
-
end
-
-
# @private
-
1
def reset_mocha
-
14
@mocha = nil
-
end
-
-
# @private
-
1
def stubba_method
-
1
Mocha::InstanceMethod
-
end
-
-
# @private
-
1
def stubba_object
-
13
self
-
end
-
-
# Adds an expectation that the specified method must be called exactly once with any parameters.
-
#
-
# The original implementation of the method is replaced during the test and then restored at the end of the test.
-
#
-
# @param [Symbol,String] method_name name of expected method
-
# @param [Hash] expected_methods_vs_return_values expected method name symbols as keys and corresponding return values as values - these expectations are setup as if {#expects} were called multiple times.
-
#
-
# @overload def expects(method_name)
-
# @overload def expects(expected_methods_vs_return_values)
-
# @return [Expectation] last-built expectation which can be further modified by methods on {Expectation}.
-
# @raise [StubbingError] if attempting to stub method which is not allowed.
-
#
-
# @example Setting up an expectation on a non-mock object.
-
# product = Product.new
-
# product.expects(:save).returns(true)
-
# assert_equal true, product.save
-
#
-
# @example Setting up multiple expectations on a non-mock object.
-
# product = Product.new
-
# product.expects(:valid? => true, :save => true)
-
#
-
# # exactly equivalent to
-
#
-
# product = Product.new
-
# product.expects(:valid?).returns(true)
-
# product.expects(:save).returns(true)
-
#
-
# @see Mock#expects
-
1
def expects(expected_methods_vs_return_values)
-
13
if expected_methods_vs_return_values.to_s =~ /the[^a-z]*spanish[^a-z]*inquisition/i
-
raise ExpectationErrorFactory.build('NOBODY EXPECTS THE SPANISH INQUISITION!')
-
end
-
13
if frozen?
-
raise StubbingError.new("can't stub method on frozen object: #{mocha_inspect}", caller)
-
end
-
13
expectation = nil
-
13
mockery = Mocha::Mockery.instance
-
13
iterator = ArgumentIterator.new(expected_methods_vs_return_values)
-
13
iterator.each { |*args|
-
13
method_name = args.shift
-
13
mockery.on_stubbing(self, method_name)
-
13
method = stubba_method.new(stubba_object, method_name)
-
13
mockery.stubba.stub(method)
-
13
expectation = mocha.expects(method_name, caller)
-
13
expectation.returns(args.shift) if args.length > 0
-
}
-
13
expectation
-
end
-
-
# Adds an expectation that the specified method may be called any number of times with any parameters.
-
#
-
# @param [Symbol,String] method_name name of stubbed method
-
# @param [Hash] stubbed_methods_vs_return_values stubbed method name symbols as keys and corresponding return values as values - these stubbed methods are setup as if {#stubs} were called multiple times.
-
#
-
# @overload def stubs(method_name)
-
# @overload def stubs(stubbed_methods_vs_return_values)
-
# @return [Expectation] last-built expectation which can be further modified by methods on {Expectation}.
-
# @raise [StubbingError] if attempting to stub method which is not allowed.
-
#
-
# @example Setting up a stubbed methods on a non-mock object.
-
# product = Product.new
-
# product.stubs(:save).returns(true)
-
# assert_equal true, product.save
-
#
-
# @example Setting up multiple stubbed methods on a non-mock object.
-
# product = Product.new
-
# product.stubs(:valid? => true, :save => true)
-
#
-
# # exactly equivalent to
-
#
-
# product = Product.new
-
# product.stubs(:valid?).returns(true)
-
# product.stubs(:save).returns(true)
-
#
-
# @see Mock#stubs
-
1
def stubs(stubbed_methods_vs_return_values)
-
1
if frozen?
-
raise StubbingError.new("can't stub method on frozen object: #{mocha_inspect}", caller)
-
end
-
1
expectation = nil
-
1
mockery = Mocha::Mockery.instance
-
1
iterator = ArgumentIterator.new(stubbed_methods_vs_return_values)
-
1
iterator.each { |*args|
-
1
method_name = args.shift
-
1
mockery.on_stubbing(self, method_name)
-
1
method = stubba_method.new(stubba_object, method_name)
-
1
mockery.stubba.stub(method)
-
1
expectation = mocha.stubs(method_name, caller)
-
1
expectation.returns(args.shift) if args.length > 0
-
}
-
1
expectation
-
end
-
-
# Removes the specified stubbed methods (added by calls to {#expects} or {#stubs}) and all expectations associated with them.
-
#
-
# Restores the original behaviour of the methods before they were stubbed.
-
#
-
# WARNING: If you {#unstub} a method which still has unsatisfied expectations, you may be removing the only way those expectations can be satisfied. Use {#unstub} with care.
-
#
-
# @param [Array<Symbol>] method_names names of methods to unstub.
-
#
-
# @example Stubbing and unstubbing a method on a real (non-mock) object.
-
# multiplier = Multiplier.new
-
# multiplier.double(2) # => 4
-
# multiplier.stubs(:double).raises # new behaviour defined
-
# multiplier.double(2) # => raises exception
-
# multiplier.unstub(:double) # original behaviour restored
-
# multiplier.double(2) # => 4
-
#
-
# @example Unstubbing multiple methods on a real (non-mock) object.
-
# multiplier.unstub(:double, :triple)
-
#
-
# # exactly equivalent to
-
#
-
# multiplier.unstub(:double)
-
# multiplier.unstub(:triple)
-
1
def unstub(*method_names)
-
mockery = Mocha::Mockery.instance
-
method_names.each do |method_name|
-
method = stubba_method.new(stubba_object, method_name)
-
mockery.stubba.unstub(method)
-
end
-
end
-
-
# @private
-
1
def method_exists?(method, include_public_methods = true)
-
if include_public_methods
-
return true if public_methods(include_superclass_methods = true).include?(method)
-
return true if respond_to?(method.to_sym)
-
end
-
return true if protected_methods(include_superclass_methods = true).include?(method)
-
return true if private_methods(include_superclass_methods = true).include?(method)
-
return false
-
end
-
-
end
-
-
end
-
1
module Mocha
-
-
# Used as parameters for {Expectation#with} to restrict the parameter values which will match the expectation. Can be nested.
-
1
module ParameterMatchers; end
-
-
end
-
-
1
require 'mocha/parameter_matchers/object'
-
-
1
require 'mocha/parameter_matchers/all_of'
-
1
require 'mocha/parameter_matchers/any_of'
-
1
require 'mocha/parameter_matchers/any_parameters'
-
1
require 'mocha/parameter_matchers/anything'
-
1
require 'mocha/parameter_matchers/equals'
-
1
require 'mocha/parameter_matchers/has_entry'
-
1
require 'mocha/parameter_matchers/has_entries'
-
1
require 'mocha/parameter_matchers/has_key'
-
1
require 'mocha/parameter_matchers/has_value'
-
1
require 'mocha/parameter_matchers/includes'
-
1
require 'mocha/parameter_matchers/instance_of'
-
1
require 'mocha/parameter_matchers/is_a'
-
1
require 'mocha/parameter_matchers/kind_of'
-
1
require 'mocha/parameter_matchers/not'
-
1
require 'mocha/parameter_matchers/optionally'
-
1
require 'mocha/parameter_matchers/regexp_matches'
-
1
require 'mocha/parameter_matchers/responds_with'
-
1
require 'mocha/parameter_matchers/yaml_equivalent'
-
1
require 'mocha/parameter_matchers/query_string'
-
1
require 'mocha/parameter_matchers/base'
-
-
1
module Mocha
-
-
1
module ParameterMatchers
-
-
# Matches if all +matchers+ match.
-
#
-
# @param [*Array<Base>] parameter_matchers parameter matchers.
-
# @return [AllOf] parameter matcher.
-
#
-
# @see Expectation#with
-
#
-
# @example All parameter matchers match.
-
# object = mock()
-
# object.expects(:method_1).with(all_of(includes(1), includes(3)))
-
# object.method_1([1, 3])
-
# # no error raised
-
#
-
# @example One of the parameter matchers does not match.
-
# object = mock()
-
# object.expects(:method_1).with(all_of(includes(1), includes(3)))
-
# object.method_1([1, 2])
-
# # error raised, because method_1 was not called with object including 1 and 3
-
1
def all_of(*matchers)
-
AllOf.new(*matchers)
-
end
-
-
# Parameter matcher which combines a number of other matchers using a logical AND.
-
1
class AllOf < Base
-
-
# @private
-
1
def initialize(*matchers)
-
@matchers = matchers
-
end
-
-
# @private
-
1
def matches?(available_parameters)
-
parameter = available_parameters.shift
-
@matchers.all? { |matcher| matcher.to_matcher.matches?([parameter]) }
-
end
-
-
# @private
-
1
def mocha_inspect
-
"all_of(#{@matchers.map { |matcher| matcher.mocha_inspect }.join(", ") })"
-
end
-
-
end
-
-
end
-
-
end
-
1
require 'mocha/parameter_matchers/base'
-
-
1
module Mocha
-
-
1
module ParameterMatchers
-
-
# Matches if any +matchers+ match.
-
#
-
# @param [*Array<Base>] parameter_matchers parameter matchers.
-
# @return [AnyOf] parameter matcher.
-
#
-
# @see Expectation#with
-
#
-
# @example One parameter matcher matches.
-
# object = mock()
-
# object.expects(:method_1).with(any_of(1, 3))
-
# object.method_1(1)
-
# # no error raised
-
#
-
# @example The other parameter matcher matches.
-
# object = mock()
-
# object.expects(:method_1).with(any_of(1, 3))
-
# object.method_1(3)
-
# # no error raised
-
#
-
# @example Neither parameter matcher matches.
-
# object = mock()
-
# object.expects(:method_1).with(any_of(1, 3))
-
# object.method_1(2)
-
# # error raised, because method_1 was not called with 1 or 3
-
1
def any_of(*matchers)
-
AnyOf.new(*matchers)
-
end
-
-
# Parameter matcher which combines a number of other matchers using a logical OR.
-
1
class AnyOf < Base
-
-
# @private
-
1
def initialize(*matchers)
-
@matchers = matchers
-
end
-
-
# @private
-
1
def matches?(available_parameters)
-
parameter = available_parameters.shift
-
@matchers.any? { |matcher| matcher.to_matcher.matches?([parameter]) }
-
end
-
-
# @private
-
1
def mocha_inspect
-
"any_of(#{@matchers.map { |matcher| matcher.mocha_inspect }.join(", ") })"
-
end
-
-
end
-
-
end
-
-
end
-
1
require 'mocha/parameter_matchers/base'
-
-
1
module Mocha
-
-
1
module ParameterMatchers
-
-
# Matches any parameters. This is used as the default for a newly built expectation.
-
#
-
# @return [AnyParameters] parameter matcher.
-
#
-
# @see Expectation#with
-
#
-
# @example Any parameters will match.
-
# object = mock()
-
# object.expects(:method_1).with(any_parameters)
-
# object.method_1(1, 2, 3, 4)
-
# # no error raised
-
#
-
# object = mock()
-
# object.expects(:method_1).with(any_parameters)
-
# object.method_1(5, 6, 7, 8, 9, 0)
-
# # no error raised
-
1
def any_parameters
-
AnyParameters.new
-
end
-
-
# Parameter matcher which always matches whatever the parameters.
-
1
class AnyParameters < Base
-
-
# @private
-
1
def matches?(available_parameters)
-
6
while available_parameters.length > 0 do
-
2
available_parameters.shift
-
end
-
6
return true
-
end
-
-
# @private
-
1
def mocha_inspect
-
"any_parameters"
-
end
-
-
end
-
-
end
-
-
end
-
1
require 'mocha/parameter_matchers/base'
-
-
1
module Mocha
-
-
1
module ParameterMatchers
-
-
# Matches any object.
-
#
-
# @return [Anything] parameter matcher.
-
#
-
# @see Expectation#with
-
#
-
# @example Any object will match.
-
# object = mock()
-
# object.expects(:method_1).with(anything)
-
# object.method_1('foo')
-
# object.method_1(789)
-
# object.method_1(:bar)
-
# # no error raised
-
1
def anything
-
Anything.new
-
end
-
-
# Parameter matcher which always matches a single parameter.
-
1
class Anything < Base
-
-
# @private
-
1
def matches?(available_parameters)
-
available_parameters.shift
-
return true
-
end
-
-
# @private
-
1
def mocha_inspect
-
"anything"
-
end
-
-
end
-
-
end
-
-
end
-
1
module Mocha
-
-
1
module ParameterMatchers
-
-
# @abstract Subclass and implement +#matches?+ and +#mocha_inspect+ to define a custom matcher. Also add a suitably named instance method to {ParameterMatchers} to build an instance of the new matcher c.f. {#equals}.
-
1
class Base
-
-
# @private
-
1
def to_matcher
-
6
self
-
end
-
-
# A shorthand way of combining two matchers when both must match.
-
#
-
# Returns a new {AllOf} parameter matcher combining two matchers using a logical AND.
-
#
-
# This shorthand will not work with an implicit equals match. Instead, an explicit {Equals} matcher should be used.
-
#
-
# @param [Base] matcher parameter matcher.
-
# @return [AllOf] parameter matcher.
-
#
-
# @see Expectation#with
-
#
-
# @example Alternative ways to combine matchers with a logical AND.
-
# object = mock()
-
# object.expects(:run).with(all_of(has_key(:foo), has_key(:bar)))
-
# object.run(:foo => 'foovalue', :bar => 'barvalue')
-
#
-
# # is exactly equivalent to
-
#
-
# object.expects(:run).with(has_key(:foo) & has_key(:bar))
-
# object.run(:foo => 'foovalue', :bar => 'barvalue)
-
1
def &(matcher)
-
AllOf.new(self, matcher)
-
end
-
-
# A shorthand way of combining two matchers when at least one must match.
-
#
-
# Returns a new +AnyOf+ parameter matcher combining two matchers using a logical OR.
-
#
-
# This shorthand will not work with an implicit equals match. Instead, an explicit {Equals} matcher should be used.
-
#
-
# @param [Base] matcher parameter matcher.
-
# @return [AnyOf] parameter matcher.
-
#
-
# @see Expectation#with
-
#
-
# @example Alternative ways to combine matchers with a logical OR.
-
# object = mock()
-
# object.expects(:run).with(any_of(has_key(:foo), has_key(:bar)))
-
# object.run(:foo => 'foovalue')
-
#
-
# # is exactly equivalent to
-
#
-
# object.expects(:run).with(has_key(:foo) | has_key(:bar))
-
# object.run(:foo => 'foovalue')
-
#
-
# @example Using an explicit {Equals} matcher in combination with {#|}.
-
# object.expects(:run).with(equals(1) | equals(2))
-
# object.run(1) # passes
-
# object.run(2) # passes
-
# object.run(3) # fails
-
1
def |(matcher)
-
AnyOf.new(self, matcher)
-
end
-
-
end
-
-
end
-
-
end
-
1
require 'mocha/parameter_matchers/base'
-
-
1
module Mocha
-
-
1
module ParameterMatchers
-
-
# Matches any +Object+ equalling +value+.
-
#
-
# @param [Object] value expected value.
-
# @return [Equals] parameter matcher.
-
#
-
# @see Expectation#with
-
# @see Object#==
-
#
-
# @example Actual parameter equals expected parameter.
-
# object = mock()
-
# object.expects(:method_1).with(equals(2))
-
# object.method_1(2)
-
# # no error raised
-
#
-
# @example Actual parameter does not equal expected parameter.
-
# object = mock()
-
# object.expects(:method_1).with(equals(2))
-
# object.method_1(3)
-
# # error raised, because method_1 was not called with an +Object+ that equals 3
-
1
def equals(value)
-
Equals.new(value)
-
end
-
-
# Parameter matcher which matches when actual parameter equals expected value.
-
1
class Equals < Base
-
-
# @private
-
1
def initialize(value)
-
8
@value = value
-
end
-
-
# @private
-
1
def matches?(available_parameters)
-
8
parameter = available_parameters.shift
-
8
parameter == @value
-
end
-
-
# @private
-
1
def mocha_inspect
-
@value.mocha_inspect
-
end
-
-
end
-
-
end
-
-
end
-
1
require 'mocha/parameter_matchers/base'
-
1
require 'mocha/parameter_matchers/all_of'
-
1
require 'mocha/parameter_matchers/has_entry'
-
-
1
module Mocha
-
-
1
module ParameterMatchers
-
-
# Matches +Hash+ containing all +entries+.
-
#
-
# @param [Hash] entries expected +Hash+ entries.
-
# @return [HasEntries] parameter matcher.
-
#
-
# @see Expectation#with
-
#
-
# @example Actual parameter contains all expected entries.
-
# object = mock()
-
# object.expects(:method_1).with(has_entries('key_1' => 1, 'key_2' => 2))
-
# object.method_1('key_1' => 1, 'key_2' => 2, 'key_3' => 3)
-
# # no error raised
-
#
-
# @example Actual parameter does not contain all expected entries.
-
# object = mock()
-
# object.expects(:method_1).with(has_entries('key_1' => 1, 'key_2' => 2))
-
# object.method_1('key_1' => 1, 'key_2' => 99)
-
# # error raised, because method_1 was not called with Hash containing entries: 'key_1' => 1, 'key_2' => 2
-
1
def has_entries(entries)
-
HasEntries.new(entries)
-
end
-
-
# Parameter matcher which matches when actual parameter contains all expected +Hash+ entries.
-
1
class HasEntries < Base
-
-
# @private
-
1
def initialize(entries)
-
@entries = entries
-
end
-
-
# @private
-
1
def matches?(available_parameters)
-
parameter = available_parameters.shift
-
has_entry_matchers = @entries.map { |key, value| HasEntry.new(key, value) }
-
AllOf.new(*has_entry_matchers).matches?([parameter])
-
end
-
-
# @private
-
1
def mocha_inspect
-
"has_entries(#{@entries.mocha_inspect})"
-
end
-
-
end
-
-
end
-
-
end
-
1
require 'mocha/parameter_matchers/base'
-
-
1
module Mocha
-
-
1
module ParameterMatchers
-
-
# Matches +Hash+ containing entry with +key+ and +value+.
-
#
-
# @overload def has_entry(key, value)
-
# @param [Object] key key for entry.
-
# @param [Object] value value for entry.
-
# @overload def has_entry(single_entry_hash)
-
# @param [Hash] single_entry_hash +Hash+ with single entry.
-
# @raise [ArgumentError] if +single_entry_hash+ does not contain exactly one entry.
-
#
-
# @return [HasEntry] parameter matcher.
-
#
-
# @see Expectation#with
-
#
-
# @example Actual parameter contains expected entry supplied as key and value.
-
# object = mock()
-
# object.expects(:method_1).with(has_entry('key_1', 1))
-
# object.method_1('key_1' => 1, 'key_2' => 2)
-
# # no error raised
-
#
-
# @example Actual parameter contains expected entry supplied as +Hash+ entry.
-
# object = mock()
-
# object.expects(:method_1).with(has_entry('key_1' => 1))
-
# object.method_1('key_1' => 1, 'key_2' => 2)
-
# # no error raised
-
#
-
# @example Actual parameter does not contain expected entry supplied as key and value.
-
# object = mock()
-
# object.expects(:method_1).with(has_entry('key_1', 1))
-
# object.method_1('key_1' => 2, 'key_2' => 1)
-
# # error raised, because method_1 was not called with Hash containing entry: 'key_1' => 1
-
#
-
# @example Actual parameter does not contain expected entry supplied as +Hash+ entry.
-
#
-
# object = mock()
-
# object.expects(:method_1).with(has_entry('key_1' => 1))
-
# object.method_1('key_1' => 2, 'key_2' => 1)
-
# # error raised, because method_1 was not called with Hash containing entry: 'key_1' => 1
-
1
def has_entry(*options)
-
key, value = options.shift, options.shift
-
if key.is_a?(Hash)
-
case key.length
-
when 0
-
raise ArgumentError.new("Argument has no entries.")
-
when 1
-
key, value = key.to_a.flatten
-
else
-
raise ArgumentError.new("Argument has multiple entries. Use Mocha::ParameterMatchers#has_entries instead.")
-
end
-
end
-
HasEntry.new(key, value)
-
end
-
-
# Parameter matcher which matches when actual parameter contains expected +Hash+ entry.
-
1
class HasEntry < Base
-
-
# @private
-
1
def initialize(key, value)
-
@key, @value = key, value
-
end
-
-
# @private
-
1
def matches?(available_parameters)
-
parameter = available_parameters.shift
-
return false unless parameter.respond_to?(:keys) && parameter.respond_to?(:[])
-
matching_keys = parameter.keys.select { |key| @key.to_matcher.matches?([key]) }
-
matching_keys.any? { |key| @value.to_matcher.matches?([parameter[key]]) }
-
end
-
-
# @private
-
1
def mocha_inspect
-
"has_entry(#{@key.mocha_inspect} => #{@value.mocha_inspect})"
-
end
-
-
end
-
-
end
-
-
end
-
1
require 'mocha/parameter_matchers/base'
-
-
1
module Mocha
-
-
1
module ParameterMatchers
-
-
# Matches +Hash+ containing +key+.
-
#
-
# @param [Object] key expected key.
-
# @return [HasKey] parameter matcher.
-
#
-
# @see Expectation#with
-
#
-
# @example Actual parameter contains entry with expected key.
-
# object = mock()
-
# object.expects(:method_1).with(has_key('key_1'))
-
# object.method_1('key_1' => 1, 'key_2' => 2)
-
# # no error raised
-
#
-
# @example Actual parameter does not contain entry with expected key.
-
# object = mock()
-
# object.expects(:method_1).with(has_key('key_1'))
-
# object.method_1('key_2' => 2)
-
# # error raised, because method_1 was not called with Hash containing key: 'key_1'
-
1
def has_key(key)
-
HasKey.new(key)
-
end
-
-
# Parameter matcher which matches when actual parameter contains +Hash+ entry with expected key.
-
1
class HasKey < Base
-
-
# @private
-
1
def initialize(key)
-
@key = key
-
end
-
-
# @private
-
1
def matches?(available_parameters)
-
parameter = available_parameters.shift
-
return false unless parameter.respond_to?(:keys)
-
parameter.keys.any? { |key| @key.to_matcher.matches?([key]) }
-
end
-
-
# @private
-
1
def mocha_inspect
-
"has_key(#{@key.mocha_inspect})"
-
end
-
-
end
-
-
end
-
-
end
-
1
require 'mocha/parameter_matchers/base'
-
-
1
module Mocha
-
-
1
module ParameterMatchers
-
-
# Matches +Hash+ containing +value+.
-
#
-
# @param [Object] value expected value.
-
# @return [HasValue] parameter matcher.
-
#
-
# @see Expectation#with
-
#
-
# @example Actual parameter contains entry with expected value.
-
# object = mock()
-
# object.expects(:method_1).with(has_value(1))
-
# object.method_1('key_1' => 1, 'key_2' => 2)
-
# # no error raised
-
#
-
# @example Actual parameter does not contain entry with expected value.
-
# object = mock()
-
# object.expects(:method_1).with(has_value(1))
-
# object.method_1('key_2' => 2)
-
# # error raised, because method_1 was not called with Hash containing value: 1
-
1
def has_value(value)
-
HasValue.new(value)
-
end
-
-
# Parameter matcher which matches when actual parameter contains +Hash+ entry with expected value.
-
1
class HasValue < Base
-
-
# @private
-
1
def initialize(value)
-
@value = value
-
end
-
-
# @private
-
1
def matches?(available_parameters)
-
parameter = available_parameters.shift
-
return false unless parameter.respond_to?(:values)
-
parameter.values.any? { |value| @value.to_matcher.matches?([value]) }
-
end
-
-
# @private
-
1
def mocha_inspect
-
"has_value(#{@value.mocha_inspect})"
-
end
-
-
end
-
-
end
-
-
end
-
1
require 'mocha/parameter_matchers/base'
-
-
1
module Mocha
-
-
1
module ParameterMatchers
-
-
# Matches any object that responds with +true+ to +include?(item)+.
-
#
-
# @param [Object] item expected item.
-
# @return [Includes] parameter matcher.
-
#
-
# @see Expectation#with
-
#
-
# @example Actual parameter includes item.
-
# object = mock()
-
# object.expects(:method_1).with(includes('foo'))
-
# object.method_1(['foo', 'bar'])
-
# # no error raised
-
#
-
# @example Actual parameter does not include item.
-
# object.method_1(['baz'])
-
# # error raised, because ['baz'] does not include 'foo'.
-
1
def includes(item)
-
Includes.new(item)
-
end
-
-
# Parameter matcher which matches when actual parameter includes expected value.
-
1
class Includes < Base
-
-
# @private
-
1
def initialize(item)
-
@item = item
-
end
-
-
# @private
-
1
def matches?(available_parameters)
-
parameter = available_parameters.shift
-
return false unless parameter.respond_to?(:include?)
-
return parameter.include?(@item)
-
end
-
-
# @private
-
1
def mocha_inspect
-
"includes(#{@item.mocha_inspect})"
-
end
-
-
end
-
-
end
-
-
end
-
1
require 'mocha/parameter_matchers/base'
-
-
1
module Mocha
-
-
1
module ParameterMatchers
-
-
# Matches any object that is an instance of +klass+
-
#
-
# @param [Class] klass expected class.
-
# @return [InstanceOf] parameter matcher.
-
#
-
# @see Expectation#with
-
# @see Kernel#instance_of?
-
#
-
# @example Actual parameter is an instance of +String+.
-
# object = mock()
-
# object.expects(:method_1).with(instance_of(String))
-
# object.method_1('string')
-
# # no error raised
-
#
-
# @example Actual parameter is not an instance of +String+.
-
# object = mock()
-
# object.expects(:method_1).with(instance_of(String))
-
# object.method_1(99)
-
# # error raised, because method_1 was not called with an instance of String
-
1
def instance_of(klass)
-
InstanceOf.new(klass)
-
end
-
-
# Parameter matcher which matches when actual parameter is an instance of the specified class.
-
1
class InstanceOf < Base
-
-
# @private
-
1
def initialize(klass)
-
@klass = klass
-
end
-
-
# @private
-
1
def matches?(available_parameters)
-
parameter = available_parameters.shift
-
parameter.instance_of?(@klass)
-
end
-
-
# @private
-
1
def mocha_inspect
-
"instance_of(#{@klass.mocha_inspect})"
-
end
-
-
end
-
-
end
-
-
end
-
1
require 'mocha/parameter_matchers/base'
-
-
1
module Mocha
-
-
1
module ParameterMatchers
-
-
# Matches any object that is a +klass+.
-
#
-
# @param [Class] klass expected class.
-
# @return [IsA] parameter matcher.
-
#
-
# @see Expectation#with
-
# @see Kernel#is_a?
-
#
-
# @example Actual parameter is a +Integer+.
-
# object = mock()
-
# object.expects(:method_1).with(is_a(Integer))
-
# object.method_1(99)
-
# # no error raised
-
#
-
# @example Actual parameter is not a +Integer+.
-
# object = mock()
-
# object.expects(:method_1).with(is_a(Integer))
-
# object.method_1('string')
-
# # error raised, because method_1 was not called with an Integer
-
1
def is_a(klass)
-
IsA.new(klass)
-
end
-
-
# Parameter matcher which matches when actual parameter is a specific class.
-
1
class IsA < Base
-
-
# @private
-
1
def initialize(klass)
-
@klass = klass
-
end
-
-
# @private
-
1
def matches?(available_parameters)
-
parameter = available_parameters.shift
-
parameter.is_a?(@klass)
-
end
-
-
# @private
-
1
def mocha_inspect
-
"is_a(#{@klass.mocha_inspect})"
-
end
-
-
end
-
-
end
-
-
end
-
1
require 'mocha/parameter_matchers/base'
-
-
1
module Mocha
-
-
1
module ParameterMatchers
-
-
# Matches any +Object+ that is a kind of +klass+.
-
#
-
# @param [Class] klass expected class.
-
# @return [KindOf] parameter matcher.
-
#
-
# @see Expectation#with
-
# @see Kernel#kind_of?
-
#
-
# @example Actual parameter is a kind of +Integer+.
-
# object = mock()
-
# object.expects(:method_1).with(kind_of(Integer))
-
# object.method_1(99)
-
# # no error raised
-
#
-
# @example Actual parameter is not a kind of +Integer+.
-
# object = mock()
-
# object.expects(:method_1).with(kind_of(Integer))
-
# object.method_1('string')
-
# # error raised, because method_1 was not called with a kind of Integer
-
1
def kind_of(klass)
-
KindOf.new(klass)
-
end
-
-
# Parameter matcher which matches when actual parameter is a kind of specified class.
-
1
class KindOf < Base
-
-
# @private
-
1
def initialize(klass)
-
@klass = klass
-
end
-
-
# @private
-
1
def matches?(available_parameters)
-
parameter = available_parameters.shift
-
parameter.kind_of?(@klass)
-
end
-
-
# @private
-
1
def mocha_inspect
-
"kind_of(#{@klass.mocha_inspect})"
-
end
-
-
end
-
-
end
-
-
end
-
1
require 'mocha/parameter_matchers/base'
-
-
1
module Mocha
-
-
1
module ParameterMatchers
-
-
# Matches if +matcher+ does *not* match.
-
#
-
# @param [Base] matcher matcher whose logic to invert.
-
# @return [Not] parameter matcher.
-
#
-
# @see Expectation#with
-
#
-
# @example Actual parameter does not include the value +1+.
-
# object = mock()
-
# object.expects(:method_1).with(Not(includes(1)))
-
# object.method_1([0, 2, 3])
-
# # no error raised
-
#
-
# @example Actual parameter does include the value +1+.
-
# object = mock()
-
# object.expects(:method_1).with(Not(includes(1)))
-
# object.method_1([0, 1, 2, 3])
-
# # error raised, because method_1 was not called with object not including 1
-
1
def Not(matcher)
-
Not.new(matcher)
-
end
-
-
# Parameter matcher which inverts the logic of the specified matcher using a logical NOT operation.
-
1
class Not < Base
-
-
# @private
-
1
def initialize(matcher)
-
@matcher = matcher
-
end
-
-
# @private
-
1
def matches?(available_parameters)
-
parameter = available_parameters.shift
-
!@matcher.matches?([parameter])
-
end
-
-
# @private
-
1
def mocha_inspect
-
"Not(#{@matcher.mocha_inspect})"
-
end
-
-
end
-
-
end
-
-
end
-
1
require 'mocha/parameter_matchers/equals'
-
-
1
module Mocha
-
-
1
module ObjectMethods
-
# @private
-
1
def to_matcher
-
8
Mocha::ParameterMatchers::Equals.new(self)
-
end
-
end
-
-
end
-
-
# @private
-
1
class Object
-
1
include Mocha::ObjectMethods
-
end
-
1
module Mocha
-
-
1
module ParameterMatchers
-
-
# Matches optional parameters if available.
-
#
-
# @param [*Array<Base>] matchers matchers for optional parameters.
-
# @return [Optionally] parameter matcher.
-
#
-
# @see Expectation#with
-
#
-
# @example Only the two required parameters are supplied and they both match their expected value.
-
# object = mock()
-
# object.expects(:method_1).with(1, 2, optionally(3, 4))
-
# object.method_1(1, 2)
-
# # no error raised
-
#
-
# @example Both required parameters and one of the optional parameters are supplied and they all match their expected value.
-
# object = mock()
-
# object.expects(:method_1).with(1, 2, optionally(3, 4))
-
# object.method_1(1, 2, 3)
-
# # no error raised
-
#
-
# @example Both required parameters and both of the optional parameters are supplied and they all match their expected value.
-
# object = mock()
-
# object.expects(:method_1).with(1, 2, optionally(3, 4))
-
# object.method_1(1, 2, 3, 4)
-
# # no error raised
-
#
-
# @example One of the actual optional parameters does not match the expected value.
-
# object = mock()
-
# object.expects(:method_1).with(1, 2, optionally(3, 4))
-
# object.method_1(1, 2, 3, 5)
-
# # error raised, because optional parameters did not match
-
1
def optionally(*matchers)
-
Optionally.new(*matchers)
-
end
-
-
# Parameter matcher which allows optional parameters to be specified.
-
1
class Optionally < Base
-
-
# @private
-
1
def initialize(*parameters)
-
@matchers = parameters.map { |parameter| parameter.to_matcher }
-
end
-
-
# @private
-
1
def matches?(available_parameters)
-
index = 0
-
while (available_parameters.length > 0) && (index < @matchers.length) do
-
matcher = @matchers[index]
-
return false unless matcher.matches?(available_parameters)
-
index += 1
-
end
-
return true
-
end
-
-
# @private
-
1
def mocha_inspect
-
"optionally(#{@matchers.map { |matcher| matcher.mocha_inspect }.join(", ") })"
-
end
-
-
end
-
-
end
-
-
end
-
1
require 'mocha/parameter_matchers/base'
-
1
require 'uri'
-
-
1
module Mocha
-
1
module ParameterMatchers
-
-
# Matches a URI without regard to the ordering of parameters in the query string.
-
#
-
# @param [String] uri URI to match.
-
# @return [QueryStringMatches] parameter matcher.
-
#
-
# @see Expectation#with
-
#
-
# @example Actual URI has equivalent query string.
-
# object = mock()
-
# object.expects(:method_1).with(has_equivalent_query_string('http://example.com/foo?a=1&b=2))
-
# object.method_1('http://example.com/foo?b=2&a=1')
-
# # no error raised
-
#
-
# @example Actual URI does not have equivalent query string.
-
# object = mock()
-
# object.expects(:method_1).with(has_equivalent_query_string('http://example.com/foo?a=1&b=2))
-
# object.method_1('http://example.com/foo?a=1&b=3')
-
# # error raised, because the query parameters were different
-
1
def has_equivalent_query_string(uri)
-
QueryStringMatches.new(uri)
-
end
-
-
# Parameter matcher which matches URIs with equivalent query strings.
-
1
class QueryStringMatches < Base
-
-
# @private
-
1
def initialize(uri)
-
@uri = URI.parse(uri)
-
end
-
-
# @private
-
1
def matches?(available_parameters)
-
actual = explode(URI.parse(available_parameters.shift))
-
expected = explode(@uri)
-
actual == expected
-
end
-
-
# @private
-
1
def mocha_inspect
-
"has_equivalent_query_string(#{@uri.mocha_inspect})"
-
end
-
-
1
private
-
# @private
-
1
def explode(uri)
-
query_hash = (uri.query || '').split('&').inject({}){ |h, kv| h.merge(Hash[*kv.split('=')]) }
-
URI::Generic::COMPONENT.inject({}){ |h, k| h.merge(k => uri.__send__(k)) }.merge(:query => query_hash)
-
end
-
-
end
-
end
-
end
-
1
require 'mocha/parameter_matchers/base'
-
-
1
module Mocha
-
-
1
module ParameterMatchers
-
-
# Matches any object that matches +regexp+.
-
#
-
# @param [Regexp] regexp regular expression to match.
-
# @return [RegexpMatches] parameter matcher.
-
#
-
# @see Expectation#with
-
#
-
# @example Actual parameter is matched by specified regular expression.
-
# object = mock()
-
# object.expects(:method_1).with(regexp_matches(/e/))
-
# object.method_1('hello')
-
# # no error raised
-
#
-
# @example Actual parameter is not matched by specified regular expression.
-
# object = mock()
-
# object.expects(:method_1).with(regexp_matches(/a/))
-
# object.method_1('hello')
-
# # error raised, because method_1 was not called with a parameter that matched the
-
# # regular expression
-
1
def regexp_matches(regexp)
-
RegexpMatches.new(regexp)
-
end
-
-
# Parameter matcher which matches if specified regular expression matches actual paramter.
-
1
class RegexpMatches < Base
-
-
# @private
-
1
def initialize(regexp)
-
@regexp = regexp
-
end
-
-
# @private
-
1
def matches?(available_parameters)
-
parameter = available_parameters.shift
-
return false unless parameter.respond_to?(:=~)
-
parameter =~ @regexp
-
end
-
-
# @private
-
1
def mocha_inspect
-
"regexp_matches(#{@regexp.mocha_inspect})"
-
end
-
-
end
-
-
end
-
-
end
-
1
require 'mocha/parameter_matchers/base'
-
1
require 'yaml'
-
-
1
module Mocha
-
-
1
module ParameterMatchers
-
-
# Matches any object that responds to +message+ with +result+. To put it another way, it tests the quack, not the duck.
-
#
-
# @param [Symbol] message method to invoke.
-
# @param [Object] result expected result of sending +message+.
-
# @return [RespondsWith] parameter matcher.
-
#
-
# @see Expectation#with
-
#
-
# @example Actual parameter responds with "FOO" when :upcase is invoked.
-
# object = mock()
-
# object.expects(:method_1).with(responds_with(:upcase, "FOO"))
-
# object.method_1("foo")
-
# # no error raised, because "foo".upcase == "FOO"
-
#
-
# @example Actual parameter does not respond with "FOO" when :upcase is invoked.
-
# object = mock()
-
# object.expects(:method_1).with(responds_with(:upcase, "BAR"))
-
# object.method_1("foo")
-
# # error raised, because "foo".upcase != "BAR"
-
1
def responds_with(message, result)
-
RespondsWith.new(message, result)
-
end
-
-
# Parameter matcher which matches if actual parameter returns expected result when specified method is invoked.
-
1
class RespondsWith < Base
-
-
# @private
-
1
def initialize(message, result)
-
@message, @result = message, result
-
end
-
-
# @private
-
1
def matches?(available_parameters)
-
parameter = available_parameters.shift
-
parameter.__send__(@message) == @result
-
end
-
-
# @private
-
1
def mocha_inspect
-
"responds_with(#{@message.mocha_inspect}, #{@result.mocha_inspect})"
-
end
-
-
end
-
-
end
-
-
end
-
1
require 'mocha/parameter_matchers/base'
-
1
require 'yaml'
-
-
1
module Mocha
-
-
1
module ParameterMatchers
-
-
# Matches any YAML that represents the specified +object+
-
#
-
# @param [Object] object object whose YAML to compare.
-
# @return [YamlEquivalent] parameter matcher.
-
#
-
# @see Expectation#with
-
#
-
# @example Actual parameter is YAML equivalent of specified +object+.
-
# object = mock()
-
# object.expects(:method_1).with(yaml_equivalent(1, 2, 3))
-
# object.method_1("--- \n- 1\n- 2\n- 3\n")
-
# # no error raised
-
#
-
# @example Actual parameter is not YAML equivalent of specified +object+.
-
# object = mock()
-
# object.expects(:method_1).with(yaml_equivalent(1, 2, 3))
-
# object.method_1("--- \n- 1\n- 2\n")
-
# # error raised, because method_1 was not called with YAML representing the specified Array
-
1
def yaml_equivalent(object)
-
YamlEquivalent.new(object)
-
end
-
-
# Parameter matcher which matches if actual parameter is YAML equivalent of specified object.
-
1
class YamlEquivalent < Base
-
-
# @private
-
1
def initialize(object)
-
@object = object
-
end
-
-
# @private
-
1
def matches?(available_parameters)
-
parameter = available_parameters.shift
-
@object == YAML.load(parameter)
-
end
-
-
# @private
-
1
def mocha_inspect
-
"yaml_equivalent(#{@object.mocha_inspect})"
-
end
-
-
end
-
-
end
-
-
end
-
1
require 'mocha/inspect'
-
1
require 'mocha/parameter_matchers'
-
-
1
module Mocha
-
-
1
class ParametersMatcher
-
-
1
def initialize(expected_parameters = [ParameterMatchers::AnyParameters.new], &matching_block)
-
22
@expected_parameters, @matching_block = expected_parameters, matching_block
-
end
-
-
1
def match?(actual_parameters = [])
-
14
if @matching_block
-
return @matching_block.call(*actual_parameters)
-
else
-
14
return parameters_match?(actual_parameters)
-
end
-
end
-
-
1
def parameters_match?(actual_parameters)
-
28
matchers.all? { |matcher| matcher.matches?(actual_parameters) } && (actual_parameters.length == 0)
-
end
-
-
1
def mocha_inspect
-
signature = matchers.mocha_inspect
-
signature = signature.gsub(/^\[|\]$/, '')
-
signature = signature.gsub(/^\{|\}$/, '') if matchers.length == 1
-
"(#{signature})"
-
end
-
-
1
def matchers
-
28
@expected_parameters.map { |parameter| parameter.to_matcher }
-
end
-
-
end
-
-
end
-
1
require 'mocha/single_return_value'
-
-
1
module Mocha
-
-
1
class ReturnValues
-
-
1
def self.build(*values)
-
4
new(*values.map { |value| SingleReturnValue.new(value) })
-
end
-
-
1
attr_accessor :values
-
-
1
def initialize(*values)
-
18
@values = values
-
end
-
-
1
def next
-
14
case @values.length
-
when 0 then nil
-
3
when 1 then @values.first.evaluate
-
else @values.shift.evaluate
-
end
-
end
-
-
1
def +(other)
-
2
self.class.new(*(@values + other.values))
-
end
-
-
end
-
-
end
-
1
module Mocha
-
-
# Used to constrain the order in which expectations can occur.
-
#
-
# @see API#sequence
-
# @see Expectation#in_sequence
-
1
class Sequence
-
-
# @private
-
1
class InSequenceOrderingConstraint
-
-
1
def initialize(sequence, index)
-
@sequence, @index = sequence, index
-
end
-
-
1
def allows_invocation_now?
-
@sequence.satisfied_to_index?(@index)
-
end
-
-
1
def mocha_inspect
-
"in sequence #{@sequence.mocha_inspect}"
-
end
-
-
end
-
-
# @private
-
1
def initialize(name)
-
@name = name
-
@expectations = []
-
end
-
-
# @private
-
1
def constrain_as_next_in_sequence(expectation)
-
index = @expectations.length
-
@expectations << expectation
-
expectation.add_ordering_constraint(InSequenceOrderingConstraint.new(self, index))
-
end
-
-
# @private
-
1
def satisfied_to_index?(index)
-
@expectations[0...index].all? { |expectation| expectation.satisfied? }
-
end
-
-
# @private
-
1
def mocha_inspect
-
"#{@name.mocha_inspect}"
-
end
-
-
end
-
-
end
-
1
require 'mocha/is_a'
-
-
1
module Mocha
-
-
1
class SingleReturnValue
-
-
1
def initialize(value)
-
2
@value = value
-
end
-
-
1
def evaluate
-
3
@value
-
end
-
-
end
-
-
end
-
1
module Mocha
-
-
1
class SingleYield
-
-
1
attr_reader :parameters
-
-
1
def initialize(*parameters)
-
@parameters = parameters
-
end
-
-
1
def each
-
yield(@parameters)
-
end
-
-
end
-
-
end
-
-
1
module Mocha
-
-
# A state machine that is used to constrain the order of invocations.
-
# An invocation can be constrained to occur when a state {#is}, or {#is_not}, active.
-
1
class StateMachine
-
-
# Provides a mechanism to change the state of a {StateMachine} at some point in the future.
-
1
class State
-
-
# @private
-
1
def initialize(state_machine, state)
-
@state_machine, @state = state_machine, state
-
end
-
-
# @private
-
1
def activate
-
@state_machine.current_state = @state
-
end
-
-
# @private
-
1
def active?
-
@state_machine.current_state == @state
-
end
-
-
# @private
-
1
def mocha_inspect
-
"#{@state_machine.name} is #{@state.mocha_inspect}"
-
end
-
-
end
-
-
# Provides the ability to determine whether a {StateMachine} is in a specified state at some point in the future.
-
1
class StatePredicate
-
-
# @private
-
1
def initialize(state_machine, state)
-
@state_machine, @state = state_machine, state
-
end
-
-
# @private
-
1
def active?
-
@state_machine.current_state != @state
-
end
-
-
# @private
-
1
def mocha_inspect
-
"#{@state_machine.name} is not #{@state.mocha_inspect}"
-
end
-
-
end
-
-
# @private
-
1
attr_reader :name
-
-
# @private
-
1
attr_accessor :current_state
-
-
# @private
-
1
def initialize(name)
-
@name = name
-
@current_state = nil
-
end
-
-
# Put the {StateMachine} into the state specified by +initial_state_name+.
-
#
-
# @param [String] initial_state_name name of initial state
-
# @return [StateMachine] state machine, thereby allowing invocations of other {StateMachine} methods to be chained.
-
1
def starts_as(initial_state_name)
-
become(initial_state_name)
-
self
-
end
-
-
# Put the {StateMachine} into the +next_state_name+.
-
#
-
# @param [String] next_state_name name of new state
-
1
def become(next_state_name)
-
@current_state = next_state_name
-
end
-
-
# Provides a mechanism to change the {StateMachine} into the state specified by +state_name+ at some point in the future.
-
#
-
# Or provides a mechanism to determine whether the {StateMachine} is in the state specified by +state_name+ at some point in the future.
-
#
-
# @param [String] state_name name of new state
-
# @return [State] state which, when activated, will change the {StateMachine} into the state with the specified +state_name+.
-
1
def is(state_name)
-
State.new(self, state_name)
-
end
-
-
# Provides a mechanism to determine whether the {StateMachine} is not in the state specified by +state_name+ at some point in the future.
-
1
def is_not(state_name)
-
StatePredicate.new(self, state_name)
-
end
-
-
# @private
-
1
def mocha_inspect
-
if @current_state
-
"#{@name} is #{@current_state.mocha_inspect}"
-
else
-
"#{@name} has no current state"
-
end
-
end
-
-
end
-
-
end
-
1
require 'mocha/backtrace_filter'
-
-
1
module Mocha
-
-
# Exception raised when stubbing a particular method is not allowed.
-
#
-
# @see Configuration.prevent
-
1
class StubbingError < StandardError
-
-
# @private
-
1
def initialize(message = nil, backtrace = [])
-
super(message)
-
filter = BacktraceFilter.new
-
set_backtrace(filter.filtered(backtrace))
-
end
-
-
end
-
-
end
-
1
module Mocha
-
-
1
class Thrower
-
-
1
def initialize(tag, object = nil)
-
@tag, @object = tag, object
-
end
-
-
1
def evaluate
-
throw @tag, @object
-
end
-
-
end
-
-
end
-
1
module Mocha
-
-
# Exception raised when an unexpected method is invoked
-
1
class UnexpectedInvocation
-
-
# @private
-
1
def initialize(mock, symbol, *arguments)
-
@mock = mock
-
@method_matcher = MethodMatcher.new(symbol)
-
@parameters_matcher = ParametersMatcher.new(arguments)
-
end
-
-
# @private
-
1
def to_s
-
method_signature = "#{@mock.mocha_inspect}.#{@method_matcher.mocha_inspect}#{@parameters_matcher.mocha_inspect}"
-
"unexpected invocation: #{method_signature}\n"
-
end
-
-
end
-
-
end
-
1
require 'mocha/no_yields'
-
1
require 'mocha/single_yield'
-
1
require 'mocha/multiple_yields'
-
-
1
module Mocha
-
-
1
class YieldParameters
-
-
1
def initialize
-
14
@parameter_groups = []
-
end
-
-
1
def next_invocation
-
2
case @parameter_groups.length
-
2
when 0 then NoYields.new
-
when 1 then @parameter_groups.first
-
else @parameter_groups.shift
-
end
-
end
-
-
1
def add(*parameters)
-
@parameter_groups << SingleYield.new(*parameters)
-
end
-
-
1
def multiple_add(*parameter_groups)
-
@parameter_groups << MultipleYields.new(*parameter_groups)
-
end
-
-
end
-
-
end
-
1
module Rack
-
-
1
class MockSession # :nodoc:
-
1
attr_writer :cookie_jar
-
1
attr_reader :default_host
-
-
1
def initialize(app, default_host = Rack::Test::DEFAULT_HOST)
-
1
@app = app
-
1
@after_request = []
-
1
@default_host = default_host
-
1
@last_request = nil
-
1
@last_response = nil
-
end
-
-
1
def after_request(&block)
-
@after_request << block
-
end
-
-
1
def clear_cookies
-
@cookie_jar = Rack::Test::CookieJar.new([], @default_host)
-
end
-
-
1
def set_cookie(cookie, uri = nil)
-
cookie_jar.merge(cookie, uri)
-
end
-
-
1
def request(uri, env)
-
1
env["HTTP_COOKIE"] ||= cookie_jar.for(uri)
-
1
@last_request = Rack::Request.new(env)
-
1
status, headers, body = @app.call(@last_request.env)
-
-
1
@last_response = MockResponse.new(status, headers, body, env["rack.errors"].flush)
-
1
body.close if body.respond_to?(:close)
-
-
1
cookie_jar.merge(last_response.headers["Set-Cookie"], uri)
-
-
1
@after_request.each { |hook| hook.call }
-
-
1
if @last_response.respond_to?(:finish)
-
1
@last_response.finish
-
else
-
@last_response
-
end
-
end
-
-
# Return the last request issued in the session. Raises an error if no
-
# requests have been sent yet.
-
1
def last_request
-
2
raise Rack::Test::Error.new("No request yet. Request a page first.") unless @last_request
-
2
@last_request
-
end
-
-
# Return the last response received in the session. Raises an error if
-
# no requests have been sent yet.
-
1
def last_response
-
4
raise Rack::Test::Error.new("No response yet. Request a page first.") unless @last_response
-
4
@last_response
-
end
-
-
1
def cookie_jar
-
2
@cookie_jar ||= Rack::Test::CookieJar.new([], @default_host)
-
end
-
-
end
-
-
end
-
1
require "uri"
-
1
require "rack"
-
1
require "rack/mock_session"
-
1
require "rack/test/cookie_jar"
-
1
require "rack/test/mock_digest_request"
-
1
require "rack/test/utils"
-
1
require "rack/test/methods"
-
1
require "rack/test/uploaded_file"
-
-
1
module Rack
-
1
module Test
-
1
VERSION = "0.6.2"
-
-
1
DEFAULT_HOST = "example.org"
-
1
MULTIPART_BOUNDARY = "----------XnJLe9ZIbbGUYtzPQJ16u1"
-
-
# The common base class for exceptions raised by Rack::Test
-
1
class Error < StandardError; end
-
-
# This class represents a series of requests issued to a Rack app, sharing
-
# a single cookie jar
-
#
-
# Rack::Test::Session's methods are most often called through Rack::Test::Methods,
-
# which will automatically build a session when it's first used.
-
1
class Session
-
1
extend Forwardable
-
1
include Rack::Test::Utils
-
-
1
def_delegators :@rack_mock_session, :clear_cookies, :set_cookie, :last_response, :last_request
-
-
# Creates a Rack::Test::Session for a given Rack app or Rack::MockSession.
-
#
-
# Note: Generally, you won't need to initialize a Rack::Test::Session directly.
-
# Instead, you should include Rack::Test::Methods into your testing context.
-
# (See README.rdoc for an example)
-
1
def initialize(mock_session)
-
1
@headers = {}
-
-
1
if mock_session.is_a?(MockSession)
-
1
@rack_mock_session = mock_session
-
else
-
@rack_mock_session = MockSession.new(mock_session)
-
end
-
-
1
@default_host = @rack_mock_session.default_host
-
end
-
-
# Issue a GET request for the given URI with the given params and Rack
-
# environment. Stores the issues request object in #last_request and
-
# the app's response in #last_response. Yield #last_response to a block
-
# if given.
-
#
-
# Example:
-
# get "/"
-
1
def get(uri, params = {}, env = {}, &block)
-
env = env_for(uri, env.merge(:method => "GET", :params => params))
-
process_request(uri, env, &block)
-
end
-
-
# Issue a POST request for the given URI. See #get
-
#
-
# Example:
-
# post "/signup", "name" => "Bryan"
-
1
def post(uri, params = {}, env = {}, &block)
-
env = env_for(uri, env.merge(:method => "POST", :params => params))
-
process_request(uri, env, &block)
-
end
-
-
# Issue a PUT request for the given URI. See #get
-
#
-
# Example:
-
# put "/"
-
1
def put(uri, params = {}, env = {}, &block)
-
env = env_for(uri, env.merge(:method => "PUT", :params => params))
-
process_request(uri, env, &block)
-
end
-
-
# Issue a PATCH request for the given URI. See #get
-
#
-
# Example:
-
# patch "/"
-
1
def patch(uri, params = {}, env = {}, &block)
-
env = env_for(uri, env.merge(:method => "PATCH", :params => params))
-
process_request(uri, env, &block)
-
end
-
-
# Issue a DELETE request for the given URI. See #get
-
#
-
# Example:
-
# delete "/"
-
1
def delete(uri, params = {}, env = {}, &block)
-
env = env_for(uri, env.merge(:method => "DELETE", :params => params))
-
process_request(uri, env, &block)
-
end
-
-
# Issue an OPTIONS request for the given URI. See #get
-
#
-
# Example:
-
# options "/"
-
1
def options(uri, params = {}, env = {}, &block)
-
env = env_for(uri, env.merge(:method => "OPTIONS", :params => params))
-
process_request(uri, env, &block)
-
end
-
-
# Issue a HEAD request for the given URI. See #get
-
#
-
# Example:
-
# head "/"
-
1
def head(uri, params = {}, env = {}, &block)
-
env = env_for(uri, env.merge(:method => "HEAD", :params => params))
-
process_request(uri, env, &block)
-
end
-
-
# Issue a request to the Rack app for the given URI and optional Rack
-
# environment. Stores the issues request object in #last_request and
-
# the app's response in #last_response. Yield #last_response to a block
-
# if given.
-
#
-
# Example:
-
# request "/"
-
1
def request(uri, env = {}, &block)
-
1
env = env_for(uri, env)
-
1
process_request(uri, env, &block)
-
end
-
-
# Set a header to be included on all subsequent requests through the
-
# session. Use a value of nil to remove a previously configured header.
-
#
-
# In accordance with the Rack spec, headers will be included in the Rack
-
# environment hash in HTTP_USER_AGENT form.
-
#
-
# Example:
-
# header "User-Agent", "Firefox"
-
1
def header(name, value)
-
if value.nil?
-
@headers.delete(name)
-
else
-
@headers[name] = value
-
end
-
end
-
-
# Set the username and password for HTTP Basic authorization, to be
-
# included in subsequent requests in the HTTP_AUTHORIZATION header.
-
#
-
# Example:
-
# basic_authorize "bryan", "secret"
-
1
def basic_authorize(username, password)
-
encoded_login = ["#{username}:#{password}"].pack("m*")
-
header('Authorization', "Basic #{encoded_login}")
-
end
-
-
1
alias_method :authorize, :basic_authorize
-
-
# Set the username and password for HTTP Digest authorization, to be
-
# included in subsequent requests in the HTTP_AUTHORIZATION header.
-
#
-
# Example:
-
# digest_authorize "bryan", "secret"
-
1
def digest_authorize(username, password)
-
@digest_username = username
-
@digest_password = password
-
end
-
-
# Rack::Test will not follow any redirects automatically. This method
-
# will follow the redirect returned (including setting the Referer header
-
# on the new request) in the last response. If the last response was not
-
# a redirect, an error will be raised.
-
1
def follow_redirect!
-
unless last_response.redirect?
-
raise Error.new("Last response was not a redirect. Cannot follow_redirect!")
-
end
-
-
get(last_response["Location"], {}, { "HTTP_REFERER" => last_request.url })
-
end
-
-
1
private
-
-
1
def env_for(path, env)
-
1
uri = URI.parse(path)
-
1
uri.path = "/#{uri.path}" unless uri.path[0] == ?/
-
1
uri.host ||= @default_host
-
-
1
env = default_env.merge(env)
-
-
1
env["HTTP_HOST"] ||= [uri.host, (uri.port if uri.port != uri.default_port)].compact.join(":")
-
-
1
env.update("HTTPS" => "on") if URI::HTTPS === uri
-
1
env["HTTP_X_REQUESTED_WITH"] = "XMLHttpRequest" if env[:xhr]
-
-
# TODO: Remove this after Rack 1.1 has been released.
-
# Stringifying and upcasing methods has be commit upstream
-
1
env["REQUEST_METHOD"] ||= env[:method] ? env[:method].to_s.upcase : "GET"
-
-
1
if env["REQUEST_METHOD"] == "GET"
-
# merge :params with the query string
-
1
if params = env[:params]
-
params = parse_nested_query(params) if params.is_a?(String)
-
params.update(parse_nested_query(uri.query))
-
uri.query = build_nested_query(params)
-
end
-
elsif !env.has_key?(:input)
-
env["CONTENT_TYPE"] ||= "application/x-www-form-urlencoded"
-
-
if env[:params].is_a?(Hash)
-
if data = build_multipart(env[:params])
-
env[:input] = data
-
env["CONTENT_LENGTH"] ||= data.length.to_s
-
env["CONTENT_TYPE"] = "multipart/form-data; boundary=#{MULTIPART_BOUNDARY}"
-
else
-
env[:input] = params_to_string(env[:params])
-
end
-
else
-
env[:input] = env[:params]
-
end
-
end
-
-
1
env.delete(:params)
-
-
1
if env.has_key?(:cookie)
-
set_cookie(env.delete(:cookie), uri)
-
end
-
-
1
Rack::MockRequest.env_for(uri.to_s, env)
-
end
-
-
1
def process_request(uri, env)
-
1
uri = URI.parse(uri)
-
1
uri.host ||= @default_host
-
-
1
@rack_mock_session.request(uri, env)
-
-
1
if retry_with_digest_auth?(env)
-
auth_env = env.merge({
-
"HTTP_AUTHORIZATION" => digest_auth_header,
-
"rack-test.digest_auth_retry" => true
-
})
-
auth_env.delete('rack.request')
-
process_request(uri.path, auth_env)
-
else
-
1
yield last_response if block_given?
-
-
1
last_response
-
end
-
end
-
-
1
def digest_auth_header
-
challenge = last_response["WWW-Authenticate"].split(" ", 2).last
-
params = Rack::Auth::Digest::Params.parse(challenge)
-
-
params.merge!({
-
"username" => @digest_username,
-
"nc" => "00000001",
-
"cnonce" => "nonsensenonce",
-
"uri" => last_request.fullpath,
-
"method" => last_request.env["REQUEST_METHOD"],
-
})
-
-
params["response"] = MockDigestRequest.new(params).response(@digest_password)
-
-
"Digest #{params}"
-
end
-
-
1
def retry_with_digest_auth?(env)
-
last_response.status == 401 &&
-
1
digest_auth_configured? &&
-
!env["rack-test.digest_auth_retry"]
-
end
-
-
1
def digest_auth_configured?
-
@digest_username
-
end
-
-
1
def default_env
-
1
{ "rack.test" => true, "REMOTE_ADDR" => "127.0.0.1" }.merge(headers_for_env)
-
end
-
-
1
def headers_for_env
-
1
converted_headers = {}
-
-
1
@headers.each do |name, value|
-
env_key = name.upcase.gsub("-", "_")
-
env_key = "HTTP_" + env_key unless "CONTENT_TYPE" == env_key
-
converted_headers[env_key] = value
-
end
-
-
1
converted_headers
-
end
-
-
1
def params_to_string(params)
-
case params
-
when Hash then build_nested_query(params)
-
when nil then ""
-
else params
-
end
-
end
-
-
end
-
-
1
def self.encoding_aware_strings?
-
defined?(Encoding) && "".respond_to?(:encode)
-
end
-
-
end
-
end
-
#
-
# = base64.rb: methods for base64-encoding and -decoding strings
-
#
-
-
# The Base64 module provides for the encoding (#encode64, #strict_encode64,
-
# #urlsafe_encode64) and decoding (#decode64, #strict_decode64,
-
# #urlsafe_decode64) of binary data using a Base64 representation.
-
#
-
# == Example
-
#
-
# A simple encoding and decoding.
-
#
-
# require "base64"
-
#
-
# enc = Base64.encode64('Send reinforcements')
-
# # -> "U2VuZCByZWluZm9yY2VtZW50cw==\n"
-
# plain = Base64.decode64(enc)
-
# # -> "Send reinforcements"
-
#
-
# The purpose of using base64 to encode data is that it translates any
-
# binary data into purely printable characters.
-
-
1
module Base64
-
1
module_function
-
-
# Returns the Base64-encoded version of +bin+.
-
# This method complies with RFC 2045.
-
# Line feeds are added to every 60 encoded charactors.
-
#
-
# require 'base64'
-
# Base64.encode64("Now is the time for all good coders\nto learn Ruby")
-
#
-
# <i>Generates:</i>
-
#
-
# Tm93IGlzIHRoZSB0aW1lIGZvciBhbGwgZ29vZCBjb2RlcnMKdG8gbGVhcm4g
-
# UnVieQ==
-
1
def encode64(bin)
-
[bin].pack("m")
-
end
-
-
# Returns the Base64-decoded version of +str+.
-
# This method complies with RFC 2045.
-
# Characters outside the base alphabet are ignored.
-
#
-
# require 'base64'
-
# str = 'VGhpcyBpcyBsaW5lIG9uZQpUaGlzIG' +
-
# 'lzIGxpbmUgdHdvClRoaXMgaXMgbGlu' +
-
# 'ZSB0aHJlZQpBbmQgc28gb24uLi4K'
-
# puts Base64.decode64(str)
-
#
-
# <i>Generates:</i>
-
#
-
# This is line one
-
# This is line two
-
# This is line three
-
# And so on...
-
1
def decode64(str)
-
str.unpack("m").first
-
end
-
-
# Returns the Base64-encoded version of +bin+.
-
# This method complies with RFC 4648.
-
# No line feeds are added.
-
1
def strict_encode64(bin)
-
[bin].pack("m0")
-
end
-
-
# Returns the Base64-decoded version of +str+.
-
# This method complies with RFC 4648.
-
# ArgumentError is raised if +str+ is incorrectly padded or contains
-
# non-alphabet characters. Note that CR or LF are also rejected.
-
1
def strict_decode64(str)
-
str.unpack("m0").first
-
end
-
-
# Returns the Base64-encoded version of +bin+.
-
# This method complies with ``Base 64 Encoding with URL and Filename Safe
-
# Alphabet'' in RFC 4648.
-
# The alphabet uses '-' instead of '+' and '_' instead of '/'.
-
1
def urlsafe_encode64(bin)
-
strict_encode64(bin).tr("+/", "-_")
-
end
-
-
# Returns the Base64-decoded version of +str+.
-
# This method complies with ``Base 64 Encoding with URL and Filename Safe
-
# Alphabet'' in RFC 4648.
-
# The alphabet uses '-' instead of '+' and '_' instead of '/'.
-
1
def urlsafe_decode64(str)
-
strict_decode64(str.tr("-_", "+/"))
-
end
-
end
-
#--
-
# benchmark.rb - a performance benchmarking library
-
#
-
# $Id: benchmark.rb 32269 2011-06-28 06:09:46Z naruse $
-
#
-
# Created by Gotoken (gotoken@notwork.org).
-
#
-
# Documentation by Gotoken (original RD), Lyle Johnson (RDoc conversion), and
-
# Gavin Sinclair (editing).
-
#++
-
#
-
# == Overview
-
#
-
# The Benchmark module provides methods for benchmarking Ruby code, giving
-
# detailed reports on the time taken for each task.
-
#
-
-
# The Benchmark module provides methods to measure and report the time
-
# used to execute Ruby code.
-
#
-
# * Measure the time to construct the string given by the expression
-
# <tt>"a"*1_000_000</tt>:
-
#
-
# require 'benchmark'
-
#
-
# puts Benchmark.measure { "a"*1_000_000 }
-
#
-
# On my machine (FreeBSD 3.2 on P5, 100MHz) this generates:
-
#
-
# 1.166667 0.050000 1.216667 ( 0.571355)
-
#
-
# This report shows the user CPU time, system CPU time, the sum of
-
# the user and system CPU times, and the elapsed real time. The unit
-
# of time is seconds.
-
#
-
# * Do some experiments sequentially using the #bm method:
-
#
-
# require 'benchmark'
-
#
-
# n = 50000
-
# Benchmark.bm do |x|
-
# x.report { for i in 1..n; a = "1"; end }
-
# x.report { n.times do ; a = "1"; end }
-
# x.report { 1.upto(n) do ; a = "1"; end }
-
# end
-
#
-
# The result:
-
#
-
# user system total real
-
# 1.033333 0.016667 1.016667 ( 0.492106)
-
# 1.483333 0.000000 1.483333 ( 0.694605)
-
# 1.516667 0.000000 1.516667 ( 0.711077)
-
#
-
# * Continuing the previous example, put a label in each report:
-
#
-
# require 'benchmark'
-
#
-
# n = 50000
-
# Benchmark.bm(7) do |x|
-
# x.report("for:") { for i in 1..n; a = "1"; end }
-
# x.report("times:") { n.times do ; a = "1"; end }
-
# x.report("upto:") { 1.upto(n) do ; a = "1"; end }
-
# end
-
#
-
# The result:
-
#
-
# user system total real
-
# for: 1.050000 0.000000 1.050000 ( 0.503462)
-
# times: 1.533333 0.016667 1.550000 ( 0.735473)
-
# upto: 1.500000 0.016667 1.516667 ( 0.711239)
-
#
-
#
-
# * The times for some benchmarks depend on the order in which items
-
# are run. These differences are due to the cost of memory
-
# allocation and garbage collection. To avoid these discrepancies,
-
# the #bmbm method is provided. For example, to compare ways to
-
# sort an array of floats:
-
#
-
# require 'benchmark'
-
#
-
# array = (1..1000000).map { rand }
-
#
-
# Benchmark.bmbm do |x|
-
# x.report("sort!") { array.dup.sort! }
-
# x.report("sort") { array.dup.sort }
-
# end
-
#
-
# The result:
-
#
-
# Rehearsal -----------------------------------------
-
# sort! 11.928000 0.010000 11.938000 ( 12.756000)
-
# sort 13.048000 0.020000 13.068000 ( 13.857000)
-
# ------------------------------- total: 25.006000sec
-
#
-
# user system total real
-
# sort! 12.959000 0.010000 12.969000 ( 13.793000)
-
# sort 12.007000 0.000000 12.007000 ( 12.791000)
-
#
-
#
-
# * Report statistics of sequential experiments with unique labels,
-
# using the #benchmark method:
-
#
-
# require 'benchmark'
-
# include Benchmark # we need the CAPTION and FORMAT constants
-
#
-
# n = 50000
-
# Benchmark.benchmark(CAPTION, 7, FORMAT, ">total:", ">avg:") do |x|
-
# tf = x.report("for:") { for i in 1..n; a = "1"; end }
-
# tt = x.report("times:") { n.times do ; a = "1"; end }
-
# tu = x.report("upto:") { 1.upto(n) do ; a = "1"; end }
-
# [tf+tt+tu, (tf+tt+tu)/3]
-
# end
-
#
-
# The result:
-
#
-
# user system total real
-
# for: 1.016667 0.016667 1.033333 ( 0.485749)
-
# times: 1.450000 0.016667 1.466667 ( 0.681367)
-
# upto: 1.533333 0.000000 1.533333 ( 0.722166)
-
# >total: 4.000000 0.033333 4.033333 ( 1.889282)
-
# >avg: 1.333333 0.011111 1.344444 ( 0.629761)
-
-
1
module Benchmark
-
-
1
BENCHMARK_VERSION = "2002-04-25" #:nodoc"
-
-
# Invokes the block with a <tt>Benchmark::Report</tt> object, which
-
# may be used to collect and report on the results of individual
-
# benchmark tests. Reserves <i>label_width</i> leading spaces for
-
# labels on each line. Prints _caption_ at the top of the
-
# report, and uses _format_ to format each line.
-
# Returns an array of Benchmark::Tms objects.
-
#
-
# If the block returns an array of
-
# <tt>Benchmark::Tms</tt> objects, these will be used to format
-
# additional lines of output. If _label_ parameters are
-
# given, these are used to label these extra lines.
-
#
-
# _Note_: Other methods provide a simpler interface to this one, and are
-
# suitable for nearly all benchmarking requirements. See the examples in
-
# Benchmark, and the #bm and #bmbm methods.
-
#
-
# Example:
-
#
-
# require 'benchmark'
-
# include Benchmark # we need the CAPTION and FORMAT constants
-
#
-
# n = 50000
-
# Benchmark.benchmark(CAPTION, 7, FORMAT, ">total:", ">avg:") do |x|
-
# tf = x.report("for:") { for i in 1..n; a = "1"; end }
-
# tt = x.report("times:") { n.times do ; a = "1"; end }
-
# tu = x.report("upto:") { 1.upto(n) do ; a = "1"; end }
-
# [tf+tt+tu, (tf+tt+tu)/3]
-
# end
-
#
-
# <i>Generates:</i>
-
#
-
# user system total real
-
# for: 1.016667 0.016667 1.033333 ( 0.485749)
-
# times: 1.450000 0.016667 1.466667 ( 0.681367)
-
# upto: 1.533333 0.000000 1.533333 ( 0.722166)
-
# >total: 4.000000 0.033333 4.033333 ( 1.889282)
-
# >avg: 1.333333 0.011111 1.344444 ( 0.629761)
-
#
-
-
1
def benchmark(caption = "", label_width = nil, format = nil, *labels) # :yield: report
-
sync = STDOUT.sync
-
STDOUT.sync = true
-
label_width ||= 0
-
label_width += 1
-
format ||= FORMAT
-
print ' '*label_width + caption
-
report = Report.new(label_width, format)
-
results = yield(report)
-
Array === results and results.grep(Tms).each {|t|
-
print((labels.shift || t.label || "").ljust(label_width), t.format(format))
-
}
-
report.list
-
ensure
-
STDOUT.sync = sync unless sync.nil?
-
end
-
-
-
# A simple interface to the #benchmark method, #bm is generates sequential reports
-
# with labels. The parameters have the same meaning as for #benchmark.
-
#
-
# require 'benchmark'
-
#
-
# n = 50000
-
# Benchmark.bm(7) do |x|
-
# x.report("for:") { for i in 1..n; a = "1"; end }
-
# x.report("times:") { n.times do ; a = "1"; end }
-
# x.report("upto:") { 1.upto(n) do ; a = "1"; end }
-
# end
-
#
-
# <i>Generates:</i>
-
#
-
# user system total real
-
# for: 1.050000 0.000000 1.050000 ( 0.503462)
-
# times: 1.533333 0.016667 1.550000 ( 0.735473)
-
# upto: 1.500000 0.016667 1.516667 ( 0.711239)
-
#
-
-
1
def bm(label_width = 0, *labels, &blk) # :yield: report
-
benchmark(CAPTION, label_width, FORMAT, *labels, &blk)
-
end
-
-
-
# Sometimes benchmark results are skewed because code executed
-
# earlier encounters different garbage collection overheads than
-
# that run later. #bmbm attempts to minimize this effect by running
-
# the tests twice, the first time as a rehearsal in order to get the
-
# runtime environment stable, the second time for
-
# real. <tt>GC.start</tt> is executed before the start of each of
-
# the real timings; the cost of this is not included in the
-
# timings. In reality, though, there's only so much that #bmbm can
-
# do, and the results are not guaranteed to be isolated from garbage
-
# collection and other effects.
-
#
-
# Because #bmbm takes two passes through the tests, it can
-
# calculate the required label width.
-
#
-
# require 'benchmark'
-
#
-
# array = (1..1000000).map { rand }
-
#
-
# Benchmark.bmbm do |x|
-
# x.report("sort!") { array.dup.sort! }
-
# x.report("sort") { array.dup.sort }
-
# end
-
#
-
# <i>Generates:</i>
-
#
-
# Rehearsal -----------------------------------------
-
# sort! 11.928000 0.010000 11.938000 ( 12.756000)
-
# sort 13.048000 0.020000 13.068000 ( 13.857000)
-
# ------------------------------- total: 25.006000sec
-
#
-
# user system total real
-
# sort! 12.959000 0.010000 12.969000 ( 13.793000)
-
# sort 12.007000 0.000000 12.007000 ( 12.791000)
-
#
-
# #bmbm yields a Benchmark::Job object and returns an array of
-
# Benchmark::Tms objects.
-
#
-
1
def bmbm(width = 0, &blk) # :yield: job
-
job = Job.new(width)
-
yield(job)
-
width = job.width + 1
-
sync = STDOUT.sync
-
STDOUT.sync = true
-
-
# rehearsal
-
puts 'Rehearsal '.ljust(width+CAPTION.length,'-')
-
ets = job.list.inject(Tms.new) { |sum,(label,item)|
-
print label.ljust(width)
-
res = Benchmark.measure(&item)
-
print res.format
-
sum + res
-
}.format("total: %tsec")
-
print " #{ets}\n\n".rjust(width+CAPTION.length+2,'-')
-
-
# take
-
print ' '*width + CAPTION
-
job.list.map { |label,item|
-
GC.start
-
print label.ljust(width)
-
Benchmark.measure(label, &item).tap { |res| print res }
-
}
-
ensure
-
STDOUT.sync = sync unless sync.nil?
-
end
-
-
#
-
# Returns the time used to execute the given block as a
-
# Benchmark::Tms object.
-
#
-
1
def measure(label = "") # :yield:
-
t0, r0 = Process.times, Time.now
-
yield
-
t1, r1 = Process.times, Time.now
-
Benchmark::Tms.new(t1.utime - t0.utime,
-
t1.stime - t0.stime,
-
t1.cutime - t0.cutime,
-
t1.cstime - t0.cstime,
-
r1.to_f - r0.to_f,
-
label)
-
end
-
-
#
-
# Returns the elapsed real time used to execute the given block.
-
#
-
1
def realtime # :yield:
-
1
r0 = Time.now
-
1
yield
-
1
Time.now - r0
-
end
-
-
1
module_function :benchmark, :measure, :realtime, :bm, :bmbm
-
-
#
-
# A Job is a sequence of labelled blocks to be processed by the
-
# Benchmark.bmbm method. It is of little direct interest to the user.
-
#
-
1
class Job # :nodoc:
-
#
-
# Returns an initialized Job instance.
-
# Usually, one doesn't call this method directly, as new
-
# Job objects are created by the #bmbm method.
-
# _width_ is a initial value for the label offset used in formatting;
-
# the #bmbm method passes its _width_ argument to this constructor.
-
#
-
1
def initialize(width)
-
@width = width
-
@list = []
-
end
-
-
#
-
# Registers the given label and block pair in the job list.
-
#
-
1
def item(label = "", &blk) # :yield:
-
raise ArgumentError, "no block" unless block_given?
-
label = label.to_s
-
w = label.length
-
@width = w if @width < w
-
@list << [label, blk]
-
self
-
end
-
-
1
alias report item
-
-
# An array of 2-element arrays, consisting of label and block pairs.
-
1
attr_reader :list
-
-
# Length of the widest label in the #list.
-
1
attr_reader :width
-
end
-
-
#
-
# This class is used by the Benchmark.benchmark and Benchmark.bm methods.
-
# It is of little direct interest to the user.
-
#
-
1
class Report # :nodoc:
-
#
-
# Returns an initialized Report instance.
-
# Usually, one doesn't call this method directly, as new
-
# Report objects are created by the #benchmark and #bm methods.
-
# _width_ and _format_ are the label offset and
-
# format string used by Tms#format.
-
#
-
1
def initialize(width = 0, format = nil)
-
@width, @format, @list = width, format, []
-
end
-
-
#
-
# Prints the _label_ and measured time for the block,
-
# formatted by _format_. See Tms#format for the
-
# formatting rules.
-
#
-
1
def item(label = "", *format, &blk) # :yield:
-
print label.to_s.ljust(@width)
-
@list << res = Benchmark.measure(label, &blk)
-
print res.format(@format, *format)
-
res
-
end
-
-
1
alias report item
-
-
# An array of Benchmark::Tms objects representing each item.
-
1
attr_reader :list
-
end
-
-
-
-
#
-
# A data object, representing the times associated with a benchmark
-
# measurement.
-
#
-
1
class Tms
-
-
# Default caption, see also Benchmark::CAPTION
-
1
CAPTION = " user system total real\n"
-
-
# Default format string, see also Benchmark::FORMAT
-
1
FORMAT = "%10.6u %10.6y %10.6t %10.6r\n"
-
-
# User CPU time
-
1
attr_reader :utime
-
-
# System CPU time
-
1
attr_reader :stime
-
-
# User CPU time of children
-
1
attr_reader :cutime
-
-
# System CPU time of children
-
1
attr_reader :cstime
-
-
# Elapsed real time
-
1
attr_reader :real
-
-
# Total time, that is _utime_ + _stime_ + _cutime_ + _cstime_
-
1
attr_reader :total
-
-
# Label
-
1
attr_reader :label
-
-
#
-
# Returns an initialized Tms object which has
-
# _utime_ as the user CPU time, _stime_ as the system CPU time,
-
# _cutime_ as the children's user CPU time, _cstime_ as the children's
-
# system CPU time, _real_ as the elapsed real time and _label_ as the label.
-
#
-
1
def initialize(utime = 0.0, stime = 0.0, cutime = 0.0, cstime = 0.0, real = 0.0, label = nil)
-
@utime, @stime, @cutime, @cstime, @real, @label = utime, stime, cutime, cstime, real, label.to_s
-
@total = @utime + @stime + @cutime + @cstime
-
end
-
-
#
-
# Returns a new Tms object whose times are the sum of the times for this
-
# Tms object, plus the time required to execute the code block (_blk_).
-
#
-
1
def add(&blk) # :yield:
-
self + Benchmark.measure(&blk)
-
end
-
-
#
-
# An in-place version of #add.
-
#
-
1
def add!(&blk)
-
t = Benchmark.measure(&blk)
-
@utime = utime + t.utime
-
@stime = stime + t.stime
-
@cutime = cutime + t.cutime
-
@cstime = cstime + t.cstime
-
@real = real + t.real
-
self
-
end
-
-
#
-
# Returns a new Tms object obtained by memberwise summation
-
# of the individual times for this Tms object with those of the other
-
# Tms object.
-
# This method and #/() are useful for taking statistics.
-
#
-
1
def +(other); memberwise(:+, other) end
-
-
#
-
# Returns a new Tms object obtained by memberwise subtraction
-
# of the individual times for the other Tms object from those of this
-
# Tms object.
-
#
-
1
def -(other); memberwise(:-, other) end
-
-
#
-
# Returns a new Tms object obtained by memberwise multiplication
-
# of the individual times for this Tms object by _x_.
-
#
-
1
def *(x); memberwise(:*, x) end
-
-
#
-
# Returns a new Tms object obtained by memberwise division
-
# of the individual times for this Tms object by _x_.
-
# This method and #+() are useful for taking statistics.
-
#
-
1
def /(x); memberwise(:/, x) end
-
-
#
-
# Returns the contents of this Tms object as
-
# a formatted string, according to a format string
-
# like that passed to Kernel.format. In addition, #format
-
# accepts the following extensions:
-
#
-
# <tt>%u</tt>:: Replaced by the user CPU time, as reported by Tms#utime.
-
# <tt>%y</tt>:: Replaced by the system CPU time, as reported by #stime (Mnemonic: y of "s*y*stem")
-
# <tt>%U</tt>:: Replaced by the children's user CPU time, as reported by Tms#cutime
-
# <tt>%Y</tt>:: Replaced by the children's system CPU time, as reported by Tms#cstime
-
# <tt>%t</tt>:: Replaced by the total CPU time, as reported by Tms#total
-
# <tt>%r</tt>:: Replaced by the elapsed real time, as reported by Tms#real
-
# <tt>%n</tt>:: Replaced by the label string, as reported by Tms#label (Mnemonic: n of "*n*ame")
-
#
-
# If _format_ is not given, FORMAT is used as default value, detailing the
-
# user, system and real elapsed time.
-
#
-
1
def format(format = nil, *args)
-
str = (format || FORMAT).dup
-
str.gsub!(/(%[-+\.\d]*)n/) { "#{$1}s" % label }
-
str.gsub!(/(%[-+\.\d]*)u/) { "#{$1}f" % utime }
-
str.gsub!(/(%[-+\.\d]*)y/) { "#{$1}f" % stime }
-
str.gsub!(/(%[-+\.\d]*)U/) { "#{$1}f" % cutime }
-
str.gsub!(/(%[-+\.\d]*)Y/) { "#{$1}f" % cstime }
-
str.gsub!(/(%[-+\.\d]*)t/) { "#{$1}f" % total }
-
str.gsub!(/(%[-+\.\d]*)r/) { "(#{$1}f)" % real }
-
format ? str % args : str
-
end
-
-
#
-
# Same as #format.
-
#
-
1
def to_s
-
format
-
end
-
-
#
-
# Returns a new 6-element array, consisting of the
-
# label, user CPU time, system CPU time, children's
-
# user CPU time, children's system CPU time and elapsed
-
# real time.
-
#
-
1
def to_a
-
[@label, @utime, @stime, @cutime, @cstime, @real]
-
end
-
-
1
protected
-
-
#
-
# Returns a new Tms object obtained by memberwise operation +op+
-
# of the individual times for this Tms object with those of the other
-
# Tms object.
-
#
-
# +op+ can be a mathematical operation such as <tt>+</tt>, <tt>-</tt>,
-
# <tt>*</tt>, <tt>/</tt>
-
#
-
1
def memberwise(op, x)
-
case x
-
when Benchmark::Tms
-
Benchmark::Tms.new(utime.__send__(op, x.utime),
-
stime.__send__(op, x.stime),
-
cutime.__send__(op, x.cutime),
-
cstime.__send__(op, x.cstime),
-
real.__send__(op, x.real)
-
)
-
else
-
Benchmark::Tms.new(utime.__send__(op, x),
-
stime.__send__(op, x),
-
cutime.__send__(op, x),
-
cstime.__send__(op, x),
-
real.__send__(op, x)
-
)
-
end
-
end
-
end
-
-
# The default caption string (heading above the output times).
-
1
CAPTION = Benchmark::Tms::CAPTION
-
-
# The default format string used to display times. See also Benchmark::Tms#format.
-
1
FORMAT = Benchmark::Tms::FORMAT
-
end
-
-
1
if __FILE__ == $0
-
include Benchmark
-
-
n = ARGV[0].to_i.nonzero? || 50000
-
puts %Q([#{n} times iterations of `a = "1"'])
-
benchmark(" " + CAPTION, 7, FORMAT) do |x|
-
x.report("for:") {for _ in 1..n; _ = "1"; end} # Benchmark.measure
-
x.report("times:") {n.times do ; _ = "1"; end}
-
x.report("upto:") {1.upto(n) do ; _ = "1"; end}
-
end
-
-
benchmark do
-
[
-
measure{for _ in 1..n; _ = "1"; end}, # Benchmark.measure
-
measure{n.times do ; _ = "1"; end},
-
measure{1.upto(n) do ; _ = "1"; end}
-
]
-
end
-
end
-
# = delegate -- Support for the Delegation Pattern
-
#
-
# Documentation by James Edward Gray II and Gavin Sinclair
-
-
##
-
# This library provides three different ways to delegate method calls to an
-
# object. The easiest to use is SimpleDelegator. Pass an object to the
-
# constructor and all methods supported by the object will be delegated. This
-
# object can be changed later.
-
#
-
# Going a step further, the top level DelegateClass method allows you to easily
-
# setup delegation through class inheritance. This is considerably more
-
# flexible and thus probably the most common use for this library.
-
#
-
# Finally, if you need full control over the delegation scheme, you can inherit
-
# from the abstract class Delegator and customize as needed. (If you find
-
# yourself needing this control, have a look at Forwardable which is also in
-
# the standard library. It may suit your needs better.)
-
#
-
# SimpleDelegator's implementation serves as a nice example if the use of
-
# Delegator:
-
#
-
# class SimpleDelegator < Delegator
-
# def initialize(obj)
-
# super # pass obj to Delegator constructor, required
-
# @delegate_sd_obj = obj # store obj for future use
-
# end
-
#
-
# def __getobj__
-
# @delegate_sd_obj # return object we are delegating to, required
-
# end
-
#
-
# def __setobj__(obj)
-
# @delegate_sd_obj = obj # change delegation object,
-
# # a feature we're providing
-
# end
-
# end
-
#
-
# == Notes
-
#
-
# Be advised, RDoc will not detect delegated methods.
-
#
-
1
class Delegator < BasicObject
-
1
kernel = ::Kernel.dup
-
1
kernel.class_eval do
-
1
[:to_s,:inspect,:=~,:!~,:===,:<=>,:eql?,:hash].each do |m|
-
8
undef_method m
-
end
-
end
-
1
include kernel
-
-
# :stopdoc:
-
1
def self.const_missing(n)
-
1
::Object.const_get(n)
-
end
-
# :startdoc:
-
-
#
-
# Pass in the _obj_ to delegate method calls to. All methods supported by
-
# _obj_ will be delegated to.
-
#
-
1
def initialize(obj)
-
__setobj__(obj)
-
end
-
-
#
-
# Handles the magic of delegation through \_\_getobj\_\_.
-
#
-
1
def method_missing(m, *args, &block)
-
207
target = self.__getobj__
-
207
begin
-
207
target.respond_to?(m) ? target.__send__(m, *args, &block) : super(m, *args, &block)
-
ensure
-
207
$@.delete_if {|t| %r"\A#{Regexp.quote(__FILE__)}:#{__LINE__-2}:"o =~ t} if $@
-
end
-
end
-
-
#
-
# Checks for a method provided by this the delegate object by forwarding the
-
# call through \_\_getobj\_\_.
-
#
-
1
def respond_to_missing?(m, include_private)
-
r = self.__getobj__.respond_to?(m, include_private)
-
if r && include_private && !self.__getobj__.respond_to?(m, false)
-
warn "#{caller(3)[0]}: delegator does not forward private method \##{m}"
-
return false
-
end
-
r
-
end
-
-
#
-
# Returns the methods available to this delegate object as the union
-
# of this object's and \_\_getobj\_\_ methods.
-
#
-
1
def methods
-
__getobj__.methods | super
-
end
-
-
#
-
# Returns the methods available to this delegate object as the union
-
# of this object's and \_\_getobj\_\_ public methods.
-
#
-
1
def public_methods(all=true)
-
__getobj__.public_methods(all) | super
-
end
-
-
#
-
# Returns the methods available to this delegate object as the union
-
# of this object's and \_\_getobj\_\_ protected methods.
-
#
-
1
def protected_methods(all=true)
-
__getobj__.protected_methods(all) | super
-
end
-
-
# Note: no need to specialize private_methods, since they are not forwarded
-
-
#
-
# Returns true if two objects are considered of equal value.
-
#
-
1
def ==(obj)
-
return true if obj.equal?(self)
-
self.__getobj__ == obj
-
end
-
-
#
-
# Returns true if two objects are not considered of equal value.
-
#
-
1
def !=(obj)
-
return false if obj.equal?(self)
-
__getobj__ != obj
-
end
-
-
1
def !
-
!__getobj__
-
end
-
-
#
-
# This method must be overridden by subclasses and should return the object
-
# method calls are being delegated to.
-
#
-
1
def __getobj__
-
raise NotImplementedError, "need to define `__getobj__'"
-
end
-
-
#
-
# This method must be overridden by subclasses and change the object delegate
-
# to _obj_.
-
#
-
1
def __setobj__(obj)
-
raise NotImplementedError, "need to define `__setobj__'"
-
end
-
-
#
-
# Serialization support for the object returned by \_\_getobj\_\_.
-
#
-
1
def marshal_dump
-
ivars = instance_variables.reject {|var| /\A@delegate_/ =~ var}
-
[
-
:__v2__,
-
ivars, ivars.map{|var| instance_variable_get(var)},
-
__getobj__
-
]
-
end
-
-
#
-
# Reinitializes delegation from a serialized object.
-
#
-
1
def marshal_load(data)
-
version, vars, values, obj = data
-
if version == :__v2__
-
vars.each_with_index{|var, i| instance_variable_set(var, values[i])}
-
__setobj__(obj)
-
else
-
__setobj__(data)
-
end
-
end
-
-
1
def initialize_clone(obj) # :nodoc:
-
self.__setobj__(obj.__getobj__.clone)
-
end
-
1
def initialize_dup(obj) # :nodoc:
-
self.__setobj__(obj.__getobj__.dup)
-
end
-
1
private :initialize_clone, :initialize_dup
-
-
##
-
# :method: trust
-
# Trust both the object returned by \_\_getobj\_\_ and self.
-
#
-
-
##
-
# :method: untrust
-
# Untrust both the object returned by \_\_getobj\_\_ and self.
-
#
-
-
##
-
# :method: taint
-
# Taint both the object returned by \_\_getobj\_\_ and self.
-
#
-
-
##
-
# :method: untaint
-
# Untaint both the object returned by \_\_getobj\_\_ and self.
-
#
-
-
##
-
# :method: freeze
-
# Freeze both the object returned by \_\_getobj\_\_ and self.
-
#
-
-
1
[:trust, :untrust, :taint, :untaint, :freeze].each do |method|
-
5
define_method method do
-
__getobj__.send(method)
-
super()
-
end
-
end
-
-
1
@delegator_api = self.public_instance_methods
-
1
def self.public_api # :nodoc:
-
2
@delegator_api
-
end
-
end
-
-
##
-
# A concrete implementation of Delegator, this class provides the means to
-
# delegate all supported method calls to the object passed into the constructor
-
# and even to change the object being delegated to at a later time with
-
# #__setobj__.
-
#
-
# Here's a simple example that takes advantage of the fact that
-
# SimpleDelegator's delegation object can be changed at any time.
-
#
-
# class Stats
-
# def initialize
-
# @source = SimpleDelegator.new([])
-
# end
-
#
-
# def stats(records)
-
# @source.__setobj__(records)
-
#
-
# "Elements: #{@source.size}\n" +
-
# " Non-Nil: #{@source.compact.size}\n" +
-
# " Unique: #{@source.uniq.size}\n"
-
# end
-
# end
-
#
-
# s = Stats.new
-
# puts s.stats(%w{James Edward Gray II})
-
# puts
-
# puts s.stats([1, 2, 3, nil, 4, 5, 1, 2])
-
#
-
# Prints:
-
#
-
# Elements: 4
-
# Non-Nil: 4
-
# Unique: 4
-
#
-
# Elements: 8
-
# Non-Nil: 7
-
# Unique: 6
-
#
-
1
class SimpleDelegator<Delegator
-
# Returns the current object method calls are being delegated to.
-
1
def __getobj__
-
@delegate_sd_obj
-
end
-
-
#
-
# Changes the delegate object to _obj_.
-
#
-
# It's important to note that this does *not* cause SimpleDelegator's methods
-
# to change. Because of this, you probably only want to change delegation
-
# to objects of the same type as the original delegate.
-
#
-
# Here's an example of changing the delegation object.
-
#
-
# names = SimpleDelegator.new(%w{James Edward Gray II})
-
# puts names[1] # => Edward
-
# names.__setobj__(%w{Gavin Sinclair})
-
# puts names[1] # => Sinclair
-
#
-
1
def __setobj__(obj)
-
raise ArgumentError, "cannot delegate to self" if self.equal?(obj)
-
@delegate_sd_obj = obj
-
end
-
end
-
-
# :stopdoc:
-
1
def Delegator.delegating_block(mid)
-
335
lambda do |*args, &block|
-
target = self.__getobj__
-
begin
-
target.__send__(mid, *args, &block)
-
ensure
-
$@.delete_if {|t| /\A#{Regexp.quote(__FILE__)}:#{__LINE__-2}:/o =~ t} if $@
-
end
-
end
-
end
-
# :startdoc:
-
-
#
-
# The primary interface to this library. Use to setup delegation when defining
-
# your class.
-
#
-
# class MyClass < DelegateClass(ClassToDelegateTo) # Step 1
-
# def initialize
-
# super(obj_of_ClassToDelegateTo) # Step 2
-
# end
-
# end
-
#
-
# Here's a sample of use from Tempfile which is really a File object with a
-
# few special rules about storage location and when the File should be
-
# deleted. That makes for an almost textbook perfect example of how to use
-
# delegation.
-
#
-
# class Tempfile < DelegateClass(File)
-
# # constant and class member data initialization...
-
#
-
# def initialize(basename, tmpdir=Dir::tmpdir)
-
# # build up file path/name in var tmpname...
-
#
-
# @tmpfile = File.open(tmpname, File::RDWR|File::CREAT|File::EXCL, 0600)
-
#
-
# # ...
-
#
-
# super(@tmpfile)
-
#
-
# # below this point, all methods of File are supported...
-
# end
-
#
-
# # ...
-
# end
-
#
-
1
def DelegateClass(superclass)
-
2
klass = Class.new(Delegator)
-
2
methods = superclass.instance_methods
-
2
methods -= ::Delegator.public_api
-
2
methods -= [:to_s,:inspect,:=~,:!~,:===]
-
2
klass.module_eval do
-
2
def __getobj__ # :nodoc:
-
@delegate_dc_obj
-
end
-
2
def __setobj__(obj) # :nodoc:
-
raise ArgumentError, "cannot delegate to self" if self.equal?(obj)
-
@delegate_dc_obj = obj
-
end
-
2
methods.each do |method|
-
335
define_method(method, Delegator.delegating_block(method))
-
end
-
end
-
2
klass.define_singleton_method :public_instance_methods do |all=true|
-
super(all) - superclass.protected_instance_methods
-
end
-
2
klass.define_singleton_method :protected_instance_methods do |all=true|
-
super(all) | superclass.protected_instance_methods
-
end
-
2
return klass
-
end
-
-
# :enddoc:
-
-
1
if __FILE__ == $0
-
class ExtArray<DelegateClass(Array)
-
def initialize()
-
super([])
-
end
-
end
-
-
ary = ExtArray.new
-
p ary.class
-
ary.push 25
-
p ary
-
ary.push 42
-
ary.each {|x| p x}
-
-
foo = Object.new
-
def foo.test
-
25
-
end
-
def foo.iter
-
yield self
-
end
-
def foo.error
-
raise 'this is OK'
-
end
-
foo2 = SimpleDelegator.new(foo)
-
p foo2
-
foo2.instance_eval{print "foo\n"}
-
p foo.test == foo2.test # => true
-
p foo2.iter{[55,true]} # => true
-
foo2.error # raise error!
-
end
-
# logger.rb - simple logging utility
-
# Copyright (C) 2000-2003, 2005, 2008, 2011 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
-
#
-
# Documentation:: NAKAMURA, Hiroshi and Gavin Sinclair
-
# License::
-
# You can redistribute it and/or modify it under the same terms of Ruby's
-
# license; either the dual license version in 2003, or any later version.
-
# Revision:: $Id: logger.rb 31641 2011-05-19 00:07:25Z nobu $
-
#
-
# A simple system for logging messages. See Logger for more documentation.
-
-
1
require 'monitor'
-
-
# == Description
-
#
-
# The Logger class provides a simple but sophisticated logging utility that
-
# you can use to output messages.
-
#
-
# The messages have associated levels, such as +INFO+ or +ERROR+ that indicate
-
# their importance. You can then give the Logger a level, and only messages
-
# at that level of higher will be printed.
-
#
-
# The levels are:
-
#
-
# +FATAL+:: an unhandleable error that results in a program crash
-
# +ERROR+:: a handleable error condition
-
# +WARN+:: a warning
-
# +INFO+:: generic (useful) information about system operation
-
# +DEBUG+:: low-level information for developers
-
#
-
# For instance, in a production system, you may have your Logger set to
-
# +INFO+ or even +WARN+
-
# When you are developing the system, however, you probably
-
# want to know about the program's internal state, and would set the Logger to
-
# +DEBUG+.
-
#
-
# *Note*: Logger does not escape or sanitize any messages passed to it.
-
# Developers should be aware of when potentially malicious data (user-input)
-
# is passed to Logger, and manually escape the untrusted data:
-
#
-
# logger.info("User-input: #{input.dump}")
-
# logger.info("User-input: %p" % input)
-
#
-
# You can use #formatter= for escaping all data.
-
#
-
# original_formatter = Logger::Formatter.new
-
# logger.formatter = proc { |severity, datetime, progname, msg|
-
# original_formatter.call(severity, datetime, progname, msg.dump)
-
# }
-
# logger.info(input)
-
#
-
# === Example
-
#
-
# This creates a logger to the standard output stream, with a level of +WARN+
-
#
-
# log = Logger.new(STDOUT)
-
# log.level = Logger::WARN
-
#
-
# log.debug("Created logger")
-
# log.info("Program started")
-
# log.warn("Nothing to do!")
-
#
-
# begin
-
# File.each_line(path) do |line|
-
# unless line =~ /^(\w+) = (.*)$/
-
# log.error("Line in wrong format: #{line}")
-
# end
-
# end
-
# rescue => err
-
# log.fatal("Caught exception; exiting")
-
# log.fatal(err)
-
# end
-
#
-
# Because the Logger's level is set to +WARN+, only the warning, error, and
-
# fatal messages are recorded. The debug and info messages are silently
-
# discarded.
-
#
-
# === Features
-
#
-
# There are several interesting features that Logger provides, like
-
# auto-rolling of log files, setting the format of log messages, and
-
# specifying a program name in conjunction with the message. The next section
-
# shows you how to achieve these things.
-
#
-
#
-
# == HOWTOs
-
#
-
# === How to create a logger
-
#
-
# The options below give you various choices, in more or less increasing
-
# complexity.
-
#
-
# 1. Create a logger which logs messages to STDERR/STDOUT.
-
#
-
# logger = Logger.new(STDERR)
-
# logger = Logger.new(STDOUT)
-
#
-
# 2. Create a logger for the file which has the specified name.
-
#
-
# logger = Logger.new('logfile.log')
-
#
-
# 3. Create a logger for the specified file.
-
#
-
# file = File.open('foo.log', File::WRONLY | File::APPEND)
-
# # To create new (and to remove old) logfile, add File::CREAT like;
-
# # file = open('foo.log', File::WRONLY | File::APPEND | File::CREAT)
-
# logger = Logger.new(file)
-
#
-
# 4. Create a logger which ages logfile once it reaches a certain size. Leave
-
# 10 "old log files" and each file is about 1,024,000 bytes.
-
#
-
# logger = Logger.new('foo.log', 10, 1024000)
-
#
-
# 5. Create a logger which ages logfile daily/weekly/monthly.
-
#
-
# logger = Logger.new('foo.log', 'daily')
-
# logger = Logger.new('foo.log', 'weekly')
-
# logger = Logger.new('foo.log', 'monthly')
-
#
-
# === How to log a message
-
#
-
# Notice the different methods (+fatal+, +error+, +info+) being used to log
-
# messages of various levels? Other methods in this family are +warn+ and
-
# +debug+. +add+ is used below to log a message of an arbitrary (perhaps
-
# dynamic) level.
-
#
-
# 1. Message in block.
-
#
-
# logger.fatal { "Argument 'foo' not given." }
-
#
-
# 2. Message as a string.
-
#
-
# logger.error "Argument #{ @foo } mismatch."
-
#
-
# 3. With progname.
-
#
-
# logger.info('initialize') { "Initializing..." }
-
#
-
# 4. With severity.
-
#
-
# logger.add(Logger::FATAL) { 'Fatal error!' }
-
#
-
# The block form allows you to create potentially complex log messages,
-
# but to delay their evaluation until and unless the message is
-
# logged. For example, if we have the following:
-
#
-
# logger.debug { "This is a " + potentially + " expensive operation" }
-
#
-
# If the logger's level is +INFO+ or higher, no debug messages will be logged,
-
# and the entire block will not even be evaluated. Compare to this:
-
#
-
# logger.debug("This is a " + potentially + " expensive operation")
-
#
-
# Here, the string concatenation is done every time, even if the log
-
# level is not set to show the debug message.
-
#
-
# === How to close a logger
-
#
-
# logger.close
-
#
-
# === Setting severity threshold
-
#
-
# 1. Original interface.
-
#
-
# logger.sev_threshold = Logger::WARN
-
#
-
# 2. Log4r (somewhat) compatible interface.
-
#
-
# logger.level = Logger::INFO
-
#
-
# DEBUG < INFO < WARN < ERROR < FATAL < UNKNOWN
-
#
-
#
-
# == Format
-
#
-
# Log messages are rendered in the output stream in a certain format by
-
# default. The default format and a sample are shown below:
-
#
-
# Log format:
-
# SeverityID, [Date Time mSec #pid] SeverityLabel -- ProgName: message
-
#
-
# Log sample:
-
# I, [Wed Mar 03 02:34:24 JST 1999 895701 #19074] INFO -- Main: info.
-
#
-
# You may change the date and time format via #datetime_format=
-
#
-
# logger.datetime_format = "%Y-%m-%d %H:%M:%S"
-
# # e.g. "2004-01-03 00:54:26"
-
#
-
# Or, you may change the overall format with #formatter= method.
-
#
-
# logger.formatter = proc do |severity, datetime, progname, msg|
-
# "#{datetime}: #{msg}\n"
-
# end
-
# # e.g. "Thu Sep 22 08:51:08 GMT+9:00 2005: hello world"
-
#
-
1
class Logger
-
1
VERSION = "1.2.7"
-
1
_, name, rev = %w$Id: logger.rb 31641 2011-05-19 00:07:25Z nobu $
-
1
if name
-
1
name = name.chomp(",v")
-
else
-
name = File.basename(__FILE__)
-
end
-
1
rev ||= "v#{VERSION}"
-
1
ProgName = "#{name}/#{rev}"
-
-
1
class Error < RuntimeError # :nodoc:
-
end
-
# not used after 1.2.7. just for compat.
-
1
class ShiftingError < Error # :nodoc:
-
end
-
-
# Logging severity.
-
1
module Severity
-
# Low-level information, mostly for developers
-
1
DEBUG = 0
-
# generic, useful information about system operation
-
1
INFO = 1
-
# a warning
-
1
WARN = 2
-
# a handleable error condition
-
1
ERROR = 3
-
# an unhandleable error that results in a program crash
-
1
FATAL = 4
-
# an unknown message that should always be logged
-
1
UNKNOWN = 5
-
end
-
1
include Severity
-
-
# Logging severity threshold (e.g. <tt>Logger::INFO</tt>).
-
1
attr_accessor :level
-
-
# program name to include in log messages.
-
1
attr_accessor :progname
-
-
# Set date-time format.
-
#
-
# +datetime_format+:: A string suitable for passing to +strftime+.
-
1
def datetime_format=(datetime_format)
-
@default_formatter.datetime_format = datetime_format
-
end
-
-
# Returns the date format being used. See #datetime_format=
-
1
def datetime_format
-
@default_formatter.datetime_format
-
end
-
-
# Logging formatter, as a +Proc+ that will take four arguments and
-
# return the formatted message. The arguments are:
-
#
-
# +severity+:: The Severity of the log message
-
# +time+:: A Time instance representing when the message was logged
-
# +progname+:: The #progname configured, or passed to the logger method
-
# +msg+:: The _Object_ the user passed to the log message; not necessarily a
-
# String.
-
#
-
# The block should return an Object that can be written to the logging
-
# device via +write+. The default formatter is used when no formatter is
-
# set.
-
1
attr_accessor :formatter
-
-
1
alias sev_threshold level
-
1
alias sev_threshold= level=
-
-
# Returns +true+ iff the current severity level allows for the printing of
-
# +DEBUG+ messages.
-
1
def debug?; @level <= DEBUG; end
-
-
# Returns +true+ iff the current severity level allows for the printing of
-
# +INFO+ messages.
-
1
def info?; @level <= INFO; end
-
-
# Returns +true+ iff the current severity level allows for the printing of
-
# +WARN+ messages.
-
1
def warn?; @level <= WARN; end
-
-
# Returns +true+ iff the current severity level allows for the printing of
-
# +ERROR+ messages.
-
1
def error?; @level <= ERROR; end
-
-
# Returns +true+ iff the current severity level allows for the printing of
-
# +FATAL+ messages.
-
1
def fatal?; @level <= FATAL; end
-
-
#
-
# === Synopsis
-
#
-
# Logger.new(name, shift_age = 7, shift_size = 1048576)
-
# Logger.new(name, shift_age = 'weekly')
-
#
-
# === Args
-
#
-
# +logdev+::
-
# The log device. This is a filename (String) or IO object (typically
-
# +STDOUT+, +STDERR+, or an open file).
-
# +shift_age+::
-
# Number of old log files to keep, *or* frequency of rotation (+daily+,
-
# +weekly+ or +monthly+).
-
# +shift_size+::
-
# Maximum logfile size (only applies when +shift_age+ is a number).
-
#
-
# === Description
-
#
-
# Create an instance.
-
#
-
1
def initialize(logdev, shift_age = 0, shift_size = 1048576)
-
2
@progname = nil
-
2
@level = DEBUG
-
2
@default_formatter = Formatter.new
-
2
@formatter = nil
-
2
@logdev = nil
-
2
if logdev
-
2
@logdev = LogDevice.new(logdev, :shift_age => shift_age,
-
:shift_size => shift_size)
-
end
-
end
-
-
#
-
# === Synopsis
-
#
-
# Logger#add(severity, message = nil, progname = nil) { ... }
-
#
-
# === Args
-
#
-
# +severity+::
-
# Severity. Constants are defined in Logger namespace: +DEBUG+, +INFO+,
-
# +WARN+, +ERROR+, +FATAL+, or +UNKNOWN+.
-
# +message+::
-
# The log message. A String or Exception.
-
# +progname+::
-
# Program name string. Can be omitted. Treated as a message if no
-
# +message+ and +block+ are given.
-
# +block+::
-
# Can be omitted. Called to get a message string if +message+ is nil.
-
#
-
# === Return
-
#
-
# +true+ if successful, +false+ otherwise.
-
#
-
# When the given severity is not high enough (for this particular logger), log
-
# no message, and return +true+.
-
#
-
# === Description
-
#
-
# Log a message if the given severity is high enough. This is the generic
-
# logging method. Users will be more inclined to use #debug, #info, #warn,
-
# #error, and #fatal.
-
#
-
# <b>Message format</b>: +message+ can be any object, but it has to be
-
# converted to a String in order to log it. Generally, +inspect+ is used
-
# if the given object is not a String.
-
# A special case is an +Exception+ object, which will be printed in detail,
-
# including message, class, and backtrace. See #msg2str for the
-
# implementation if required.
-
#
-
# === Bugs
-
#
-
# * Logfile is not locked.
-
# * Append open does not need to lock file.
-
# * If the OS which supports multi I/O, records possibly be mixed.
-
#
-
1
def add(severity, message = nil, progname = nil, &block)
-
severity ||= UNKNOWN
-
if @logdev.nil? or severity < @level
-
return true
-
end
-
progname ||= @progname
-
if message.nil?
-
if block_given?
-
message = yield
-
else
-
message = progname
-
progname = @progname
-
end
-
end
-
@logdev.write(
-
format_message(format_severity(severity), Time.now, progname, message))
-
true
-
end
-
1
alias log add
-
-
#
-
# Dump given message to the log device without any formatting. If no log
-
# device exists, return +nil+.
-
#
-
1
def <<(msg)
-
unless @logdev.nil?
-
@logdev.write(msg)
-
end
-
end
-
-
#
-
# Log a +DEBUG+ message.
-
#
-
# See #info for more information.
-
#
-
1
def debug(progname = nil, &block)
-
add(DEBUG, nil, progname, &block)
-
end
-
-
#
-
# :call-seq:
-
# info(message)
-
# info(progname,&block)
-
#
-
# Log an +INFO+ message.
-
#
-
# +message+:: the message to log; does not need to be a String
-
# +progname+:: in the block form, this is the #progname to use in the
-
# the log message. The default can be set with #progname=
-
# <tt>&block</tt>:: evaluates to the message to log. This is not evaluated
-
# unless the logger's level is sufficient
-
# to log the message. This allows you to create
-
# potentially expensive logging messages that are
-
# only called when the logger is configured to show them.
-
#
-
# === Examples
-
#
-
# logger.info("MainApp") { "Received connection from #{ip}" }
-
# # ...
-
# logger.info "Waiting for input from user"
-
# # ...
-
# logger.info { "User typed #{input}" }
-
#
-
# You'll probably stick to the second form above, unless you want to provide a
-
# program name (which you can do with #progname= as well).
-
#
-
# === Return
-
#
-
# See #add.
-
#
-
1
def info(progname = nil, &block)
-
add(INFO, nil, progname, &block)
-
end
-
-
#
-
# Log a +WARN+ message.
-
#
-
# See #info for more information.
-
#
-
1
def warn(progname = nil, &block)
-
add(WARN, nil, progname, &block)
-
end
-
-
#
-
# Log an +ERROR+ message.
-
#
-
# See #info for more information.
-
#
-
1
def error(progname = nil, &block)
-
add(ERROR, nil, progname, &block)
-
end
-
-
#
-
# Log a +FATAL+ message.
-
#
-
# See #info for more information.
-
#
-
1
def fatal(progname = nil, &block)
-
add(FATAL, nil, progname, &block)
-
end
-
-
#
-
# Log an +UNKNOWN+ message. This will be printed no matter what the logger's
-
# level.
-
#
-
# See #info for more information.
-
#
-
1
def unknown(progname = nil, &block)
-
add(UNKNOWN, nil, progname, &block)
-
end
-
-
#
-
# Close the logging device.
-
#
-
1
def close
-
@logdev.close if @logdev
-
end
-
-
1
private
-
-
# Severity label for logging. (max 5 char)
-
1
SEV_LABEL = %w(DEBUG INFO WARN ERROR FATAL ANY)
-
-
1
def format_severity(severity)
-
SEV_LABEL[severity] || 'ANY'
-
end
-
-
1
def format_message(severity, datetime, progname, msg)
-
(@formatter || @default_formatter).call(severity, datetime, progname, msg)
-
end
-
-
-
# Default formatter for log messages
-
1
class Formatter
-
1
Format = "%s, [%s#%d] %5s -- %s: %s\n"
-
-
1
attr_accessor :datetime_format
-
-
1
def initialize
-
4
@datetime_format = nil
-
end
-
-
1
def call(severity, time, progname, msg)
-
Format % [severity[0..0], format_datetime(time), $$, severity, progname,
-
msg2str(msg)]
-
end
-
-
1
private
-
-
1
def format_datetime(time)
-
if @datetime_format.nil?
-
time.strftime("%Y-%m-%dT%H:%M:%S.") << "%06d " % time.usec
-
else
-
time.strftime(@datetime_format)
-
end
-
end
-
-
1
def msg2str(msg)
-
case msg
-
when ::String
-
msg
-
when ::Exception
-
"#{ msg.message } (#{ msg.class })\n" <<
-
(msg.backtrace || []).join("\n")
-
else
-
msg.inspect
-
end
-
end
-
end
-
-
-
# Device used for logging messages.
-
1
class LogDevice
-
1
attr_reader :dev
-
1
attr_reader :filename
-
-
1
class LogDeviceMutex
-
1
include MonitorMixin
-
end
-
-
1
def initialize(log = nil, opt = {})
-
2
@dev = @filename = @shift_age = @shift_size = nil
-
2
@mutex = LogDeviceMutex.new
-
2
if log.respond_to?(:write) and log.respond_to?(:close)
-
2
@dev = log
-
else
-
@dev = open_logfile(log)
-
@dev.sync = true
-
@filename = log
-
@shift_age = opt[:shift_age] || 7
-
@shift_size = opt[:shift_size] || 1048576
-
end
-
end
-
-
1
def write(message)
-
begin
-
@mutex.synchronize do
-
if @shift_age and @dev.respond_to?(:stat)
-
begin
-
check_shift_log
-
rescue
-
warn("log shifting failed. #{$!}")
-
end
-
end
-
begin
-
@dev.write(message)
-
rescue
-
warn("log writing failed. #{$!}")
-
end
-
end
-
rescue Exception => ignored
-
warn("log writing failed. #{ignored}")
-
end
-
end
-
-
1
def close
-
begin
-
@mutex.synchronize do
-
@dev.close rescue nil
-
end
-
rescue Exception
-
@dev.close rescue nil
-
end
-
end
-
-
1
private
-
-
1
def open_logfile(filename)
-
if (FileTest.exist?(filename))
-
open(filename, (File::WRONLY | File::APPEND))
-
else
-
create_logfile(filename)
-
end
-
end
-
-
1
def create_logfile(filename)
-
logdev = open(filename, (File::WRONLY | File::APPEND | File::CREAT))
-
logdev.sync = true
-
add_log_header(logdev)
-
logdev
-
end
-
-
1
def add_log_header(file)
-
file.write(
-
"# Logfile created on %s by %s\n" % [Time.now.to_s, Logger::ProgName]
-
)
-
end
-
-
1
SiD = 24 * 60 * 60
-
-
1
def check_shift_log
-
if @shift_age.is_a?(Integer)
-
# Note: always returns false if '0'.
-
if @filename && (@shift_age > 0) && (@dev.stat.size > @shift_size)
-
shift_log_age
-
end
-
else
-
now = Time.now
-
period_end = previous_period_end(now)
-
if @dev.stat.mtime <= period_end
-
shift_log_period(period_end)
-
end
-
end
-
end
-
-
1
def shift_log_age
-
(@shift_age-3).downto(0) do |i|
-
if FileTest.exist?("#{@filename}.#{i}")
-
File.rename("#{@filename}.#{i}", "#{@filename}.#{i+1}")
-
end
-
end
-
@dev.close rescue nil
-
File.rename("#{@filename}", "#{@filename}.0")
-
@dev = create_logfile(@filename)
-
return true
-
end
-
-
1
def shift_log_period(period_end)
-
postfix = period_end.strftime("%Y%m%d") # YYYYMMDD
-
age_file = "#{@filename}.#{postfix}"
-
if FileTest.exist?(age_file)
-
# try to avoid filename crash caused by Timestamp change.
-
idx = 0
-
# .99 can be overridden; avoid too much file search with 'loop do'
-
while idx < 100
-
idx += 1
-
age_file = "#{@filename}.#{postfix}.#{idx}"
-
break unless FileTest.exist?(age_file)
-
end
-
end
-
@dev.close rescue nil
-
File.rename("#{@filename}", age_file)
-
@dev = create_logfile(@filename)
-
return true
-
end
-
-
1
def previous_period_end(now)
-
case @shift_age
-
when /^daily$/
-
eod(now - 1 * SiD)
-
when /^weekly$/
-
eod(now - ((now.wday + 1) * SiD))
-
when /^monthly$/
-
eod(now - now.mday * SiD)
-
else
-
now
-
end
-
end
-
-
1
def eod(t)
-
Time.mktime(t.year, t.month, t.mday, 23, 59, 59)
-
end
-
end
-
-
-
#
-
# == Description
-
#
-
# Application -- Add logging support to your application.
-
#
-
# == Usage
-
#
-
# 1. Define your application class as a sub-class of this class.
-
# 2. Override 'run' method in your class to do many things.
-
# 3. Instantiate it and invoke 'start'.
-
#
-
# == Example
-
#
-
# class FooApp < Application
-
# def initialize(foo_app, application_specific, arguments)
-
# super('FooApp') # Name of the application.
-
# end
-
#
-
# def run
-
# ...
-
# log(WARN, 'warning', 'my_method1')
-
# ...
-
# @log.error('my_method2') { 'Error!' }
-
# ...
-
# end
-
# end
-
#
-
# status = FooApp.new(....).start
-
#
-
1
class Application
-
1
include Logger::Severity
-
-
# Name of the application given at initialize.
-
1
attr_reader :appname
-
-
#
-
# == Synopsis
-
#
-
# Application.new(appname = '')
-
#
-
# == Args
-
#
-
# +appname+:: Name of the application.
-
#
-
# == Description
-
#
-
# Create an instance. Log device is +STDERR+ by default. This can be
-
# changed with #set_log.
-
#
-
1
def initialize(appname = nil)
-
@appname = appname
-
@log = Logger.new(STDERR)
-
@log.progname = @appname
-
@level = @log.level
-
end
-
-
#
-
# Start the application. Return the status code.
-
#
-
1
def start
-
status = -1
-
begin
-
log(INFO, "Start of #{ @appname }.")
-
status = run
-
rescue
-
log(FATAL, "Detected an exception. Stopping ... #{$!} (#{$!.class})\n" << $@.join("\n"))
-
ensure
-
log(INFO, "End of #{ @appname }. (status: #{ status.to_s })")
-
end
-
status
-
end
-
-
# Logger for this application. See the class Logger for an explanation.
-
1
def logger
-
@log
-
end
-
-
#
-
# Sets the logger for this application. See the class Logger for an
-
# explanation.
-
#
-
1
def logger=(logger)
-
@log = logger
-
@log.progname = @appname
-
@log.level = @level
-
end
-
-
#
-
# Sets the log device for this application. See <tt>Logger.new</tt> for
-
# an explanation of the arguments.
-
#
-
1
def set_log(logdev, shift_age = 0, shift_size = 1024000)
-
@log = Logger.new(logdev, shift_age, shift_size)
-
@log.progname = @appname
-
@log.level = @level
-
end
-
-
1
def log=(logdev)
-
set_log(logdev)
-
end
-
-
#
-
# Set the logging threshold, just like <tt>Logger#level=</tt>.
-
#
-
1
def level=(level)
-
@level = level
-
@log.level = @level
-
end
-
-
#
-
# See Logger#add. This application's +appname+ is used.
-
#
-
1
def log(severity, message = nil, &block)
-
@log.add(severity, message, @appname, &block) if @log
-
end
-
-
1
private
-
-
1
def run
-
# TODO: should be an NotImplementedError
-
raise RuntimeError.new('Method run must be defined in the derived class.')
-
end
-
end
-
end
-
#
-
# mutex_m.rb -
-
# $Release Version: 3.0$
-
# $Revision: 1.7 $
-
# Original from mutex.rb
-
# by Keiju ISHITSUKA(keiju@ishitsuka.com)
-
# modified by matz
-
# patched by akira yamada
-
#
-
# --
-
# Usage:
-
# require "mutex_m.rb"
-
# obj = Object.new
-
# obj.extend Mutex_m
-
# ...
-
# extended object can be handled like Mutex
-
# or
-
# class Foo
-
# include Mutex_m
-
# ...
-
# end
-
# obj = Foo.new
-
# this obj can be handled like Mutex
-
#
-
-
1
require 'thread'
-
-
1
module Mutex_m
-
1
def Mutex_m.define_aliases(cl)
-
3
cl.module_eval %q{
-
alias locked? mu_locked?
-
alias lock mu_lock
-
alias unlock mu_unlock
-
alias try_lock mu_try_lock
-
alias synchronize mu_synchronize
-
}
-
end
-
-
1
def Mutex_m.append_features(cl)
-
2
super
-
2
define_aliases(cl) unless cl.instance_of?(Module)
-
end
-
-
1
def Mutex_m.extend_object(obj)
-
1
super
-
1
obj.mu_extended
-
end
-
-
1
def mu_extended
-
unless (defined? locked? and
-
1
defined? lock and
-
defined? unlock and
-
defined? try_lock and
-
defined? synchronize)
-
1
Mutex_m.define_aliases(singleton_class)
-
end
-
1
mu_initialize
-
end
-
-
# locking
-
1
def mu_synchronize(&block)
-
728
@_mutex.synchronize(&block)
-
end
-
-
1
def mu_locked?
-
@_mutex.locked?
-
end
-
-
1
def mu_try_lock
-
@_mutex.try_lock
-
end
-
-
1
def mu_lock
-
@_mutex.lock
-
end
-
-
1
def mu_unlock
-
@_mutex.unlock
-
end
-
-
1
def sleep(timeout = nil)
-
@_mutex.sleep(timeout)
-
end
-
-
1
private
-
-
1
def mu_initialize
-
74
@_mutex = Mutex.new
-
end
-
-
1
def initialize(*args)
-
73
mu_initialize
-
73
super
-
end
-
end
-
#
-
# = net/protocol.rb
-
#
-
#--
-
# Copyright (c) 1999-2004 Yukihiro Matsumoto
-
# Copyright (c) 1999-2004 Minero Aoki
-
#
-
# written and maintained by Minero Aoki <aamine@loveruby.net>
-
#
-
# This program is free software. You can re-distribute and/or
-
# modify this program under the same terms as Ruby itself,
-
# Ruby Distribute License or GNU General Public License.
-
#
-
# $Id: protocol.rb 31860 2011-05-31 08:10:42Z nahi $
-
#++
-
#
-
# WARNING: This file is going to remove.
-
# Do not rely on the implementation written in this file.
-
#
-
-
1
require 'socket'
-
1
require 'timeout'
-
-
1
module Net # :nodoc:
-
-
1
class Protocol #:nodoc: internal use only
-
1
private
-
1
def Protocol.protocol_param(name, val)
-
module_eval(<<-End, __FILE__, __LINE__ + 1)
-
def #{name}
-
#{val}
-
end
-
End
-
end
-
end
-
-
-
1
class ProtocolError < StandardError; end
-
1
class ProtoSyntaxError < ProtocolError; end
-
1
class ProtoFatalError < ProtocolError; end
-
1
class ProtoUnknownError < ProtocolError; end
-
1
class ProtoServerError < ProtocolError; end
-
1
class ProtoAuthError < ProtocolError; end
-
1
class ProtoCommandError < ProtocolError; end
-
1
class ProtoRetriableError < ProtocolError; end
-
1
ProtocRetryError = ProtoRetriableError
-
-
-
1
class BufferedIO #:nodoc: internal use only
-
1
def initialize(io)
-
@io = io
-
@read_timeout = 60
-
@continue_timeout = nil
-
@debug_output = nil
-
@rbuf = ''
-
end
-
-
1
attr_reader :io
-
1
attr_accessor :read_timeout
-
1
attr_accessor :continue_timeout
-
1
attr_accessor :debug_output
-
-
1
def inspect
-
"#<#{self.class} io=#{@io}>"
-
end
-
-
1
def eof?
-
@io.eof?
-
end
-
-
1
def closed?
-
@io.closed?
-
end
-
-
1
def close
-
@io.close
-
end
-
-
#
-
# Read
-
#
-
-
1
public
-
-
1
def read(len, dest = '', ignore_eof = false)
-
LOG "reading #{len} bytes..."
-
read_bytes = 0
-
begin
-
while read_bytes + @rbuf.size < len
-
dest << (s = rbuf_consume(@rbuf.size))
-
read_bytes += s.size
-
rbuf_fill
-
end
-
dest << (s = rbuf_consume(len - read_bytes))
-
read_bytes += s.size
-
rescue EOFError
-
raise unless ignore_eof
-
end
-
LOG "read #{read_bytes} bytes"
-
dest
-
end
-
-
1
def read_all(dest = '')
-
LOG 'reading all...'
-
read_bytes = 0
-
begin
-
while true
-
dest << (s = rbuf_consume(@rbuf.size))
-
read_bytes += s.size
-
rbuf_fill
-
end
-
rescue EOFError
-
;
-
end
-
LOG "read #{read_bytes} bytes"
-
dest
-
end
-
-
1
def readuntil(terminator, ignore_eof = false)
-
begin
-
until idx = @rbuf.index(terminator)
-
rbuf_fill
-
end
-
return rbuf_consume(idx + terminator.size)
-
rescue EOFError
-
raise unless ignore_eof
-
return rbuf_consume(@rbuf.size)
-
end
-
end
-
-
1
def readline
-
readuntil("\n").chop
-
end
-
-
1
private
-
-
1
BUFSIZE = 1024 * 16
-
-
1
def rbuf_fill
-
begin
-
@rbuf << @io.read_nonblock(BUFSIZE)
-
rescue IO::WaitReadable
-
if IO.select([@io], nil, nil, @read_timeout)
-
retry
-
else
-
raise Timeout::Error
-
end
-
rescue IO::WaitWritable
-
# OpenSSL::Buffering#read_nonblock may fail with IO::WaitWritable.
-
# http://www.openssl.org/support/faq.html#PROG10
-
if IO.select(nil, [@io], nil, @read_timeout)
-
retry
-
else
-
raise Timeout::Error
-
end
-
end
-
end
-
-
1
def rbuf_consume(len)
-
s = @rbuf.slice!(0, len)
-
@debug_output << %Q[-> #{s.dump}\n] if @debug_output
-
s
-
end
-
-
#
-
# Write
-
#
-
-
1
public
-
-
1
def write(str)
-
writing {
-
write0 str
-
}
-
end
-
-
1
alias << write
-
-
1
def writeline(str)
-
writing {
-
write0 str + "\r\n"
-
}
-
end
-
-
1
private
-
-
1
def writing
-
@written_bytes = 0
-
@debug_output << '<- ' if @debug_output
-
yield
-
@debug_output << "\n" if @debug_output
-
bytes = @written_bytes
-
@written_bytes = nil
-
bytes
-
end
-
-
1
def write0(str)
-
@debug_output << str.dump if @debug_output
-
len = @io.write(str)
-
@written_bytes += len
-
len
-
end
-
-
#
-
# Logging
-
#
-
-
1
private
-
-
1
def LOG_off
-
@save_debug_out = @debug_output
-
@debug_output = nil
-
end
-
-
1
def LOG_on
-
@debug_output = @save_debug_out
-
end
-
-
1
def LOG(msg)
-
return unless @debug_output
-
@debug_output << msg + "\n"
-
end
-
end
-
-
-
1
class InternetMessageIO < BufferedIO #:nodoc: internal use only
-
1
def initialize(io)
-
super
-
@wbuf = nil
-
end
-
-
#
-
# Read
-
#
-
-
1
def each_message_chunk
-
LOG 'reading message...'
-
LOG_off()
-
read_bytes = 0
-
while (line = readuntil("\r\n")) != ".\r\n"
-
read_bytes += line.size
-
yield line.sub(/\A\./, '')
-
end
-
LOG_on()
-
LOG "read message (#{read_bytes} bytes)"
-
end
-
-
# *library private* (cannot handle 'break')
-
1
def each_list_item
-
while (str = readuntil("\r\n")) != ".\r\n"
-
yield str.chop
-
end
-
end
-
-
1
def write_message_0(src)
-
prev = @written_bytes
-
each_crlf_line(src) do |line|
-
write0 line.sub(/\A\./, '..')
-
end
-
@written_bytes - prev
-
end
-
-
#
-
# Write
-
#
-
-
1
def write_message(src)
-
LOG "writing message from #{src.class}"
-
LOG_off()
-
len = writing {
-
using_each_crlf_line {
-
write_message_0 src
-
}
-
}
-
LOG_on()
-
LOG "wrote #{len} bytes"
-
len
-
end
-
-
1
def write_message_by_block(&block)
-
LOG 'writing message from block'
-
LOG_off()
-
len = writing {
-
using_each_crlf_line {
-
begin
-
block.call(WriteAdapter.new(self, :write_message_0))
-
rescue LocalJumpError
-
# allow `break' from writer block
-
end
-
}
-
}
-
LOG_on()
-
LOG "wrote #{len} bytes"
-
len
-
end
-
-
1
private
-
-
1
def using_each_crlf_line
-
@wbuf = ''
-
yield
-
if not @wbuf.empty? # unterminated last line
-
write0 @wbuf.chomp + "\r\n"
-
elsif @written_bytes == 0 # empty src
-
write0 "\r\n"
-
end
-
write0 ".\r\n"
-
@wbuf = nil
-
end
-
-
1
def each_crlf_line(src)
-
buffer_filling(@wbuf, src) do
-
while line = @wbuf.slice!(/\A.*(?:\n|\r\n|\r(?!\z))/n)
-
yield line.chomp("\n") + "\r\n"
-
end
-
end
-
end
-
-
1
def buffer_filling(buf, src)
-
case src
-
when String # for speeding up.
-
0.step(src.size - 1, 1024) do |i|
-
buf << src[i, 1024]
-
yield
-
end
-
when File # for speeding up.
-
while s = src.read(1024)
-
buf << s
-
yield
-
end
-
else # generic reader
-
src.each do |str|
-
buf << str
-
yield if buf.size > 1024
-
end
-
yield unless buf.empty?
-
end
-
end
-
end
-
-
-
#
-
# The writer adapter class
-
#
-
1
class WriteAdapter
-
1
def initialize(socket, method)
-
@socket = socket
-
@method_id = method
-
end
-
-
1
def inspect
-
"#<#{self.class} socket=#{@socket.inspect}>"
-
end
-
-
1
def write(str)
-
@socket.__send__(@method_id, str)
-
end
-
-
1
alias print write
-
-
1
def <<(str)
-
write str
-
self
-
end
-
-
1
def puts(str = '')
-
write str.chomp("\n") + "\n"
-
end
-
-
1
def printf(*args)
-
write sprintf(*args)
-
end
-
end
-
-
-
1
class ReadAdapter #:nodoc: internal use only
-
1
def initialize(block)
-
@block = block
-
end
-
-
1
def inspect
-
"#<#{self.class}>"
-
end
-
-
1
def <<(str)
-
call_block(str, &@block) if @block
-
end
-
-
1
private
-
-
# This method is needed because @block must be called by yield,
-
# not Proc#call. You can see difference when using `break' in
-
# the block.
-
1
def call_block(str)
-
yield str
-
end
-
end
-
-
-
1
module NetPrivate #:nodoc: obsolete
-
1
Socket = ::Net::InternetMessageIO
-
end
-
-
end # module Net
-
# = net/smtp.rb
-
#
-
# Copyright (c) 1999-2007 Yukihiro Matsumoto.
-
#
-
# Copyright (c) 1999-2007 Minero Aoki.
-
#
-
# Written & maintained by Minero Aoki <aamine@loveruby.net>.
-
#
-
# Documented by William Webber and Minero Aoki.
-
#
-
# This program is free software. You can re-distribute and/or
-
# modify this program under the same terms as Ruby itself.
-
#
-
# NOTE: You can find Japanese version of this document at:
-
# http://www.ruby-lang.org/ja/man/html/net_smtp.html
-
#
-
# $Id: smtp.rb 31710 2011-05-23 00:21:10Z drbrain $
-
#
-
# See Net::SMTP for documentation.
-
#
-
-
1
require 'net/protocol'
-
1
require 'digest/md5'
-
1
require 'timeout'
-
1
begin
-
1
require 'openssl'
-
rescue LoadError
-
end
-
-
1
module Net
-
-
# Module mixed in to all SMTP error classes
-
1
module SMTPError
-
# This *class* is a module for backward compatibility.
-
# In later release, this module becomes a class.
-
end
-
-
# Represents an SMTP authentication error.
-
1
class SMTPAuthenticationError < ProtoAuthError
-
1
include SMTPError
-
end
-
-
# Represents SMTP error code 420 or 450, a temporary error.
-
1
class SMTPServerBusy < ProtoServerError
-
1
include SMTPError
-
end
-
-
# Represents an SMTP command syntax error (error code 500)
-
1
class SMTPSyntaxError < ProtoSyntaxError
-
1
include SMTPError
-
end
-
-
# Represents a fatal SMTP error (error code 5xx, except for 500)
-
1
class SMTPFatalError < ProtoFatalError
-
1
include SMTPError
-
end
-
-
# Unexpected reply code returned from server.
-
1
class SMTPUnknownError < ProtoUnknownError
-
1
include SMTPError
-
end
-
-
# Command is not supported on server.
-
1
class SMTPUnsupportedCommand < ProtocolError
-
1
include SMTPError
-
end
-
-
#
-
# = Net::SMTP
-
#
-
# == What is This Library?
-
#
-
# This library provides functionality to send internet
-
# mail via SMTP, the Simple Mail Transfer Protocol. For details of
-
# SMTP itself, see [RFC2821] (http://www.ietf.org/rfc/rfc2821.txt).
-
#
-
# == What is This Library NOT?
-
#
-
# This library does NOT provide functions to compose internet mails.
-
# You must create them by yourself. If you want better mail support,
-
# try RubyMail or TMail. You can get both libraries from RAA.
-
# (http://www.ruby-lang.org/en/raa.html)
-
#
-
# FYI: the official documentation on internet mail is: [RFC2822] (http://www.ietf.org/rfc/rfc2822.txt).
-
#
-
# == Examples
-
#
-
# === Sending Messages
-
#
-
# You must open a connection to an SMTP server before sending messages.
-
# The first argument is the address of your SMTP server, and the second
-
# argument is the port number. Using SMTP.start with a block is the simplest
-
# way to do this. This way, the SMTP connection is closed automatically
-
# after the block is executed.
-
#
-
# require 'net/smtp'
-
# Net::SMTP.start('your.smtp.server', 25) do |smtp|
-
# # Use the SMTP object smtp only in this block.
-
# end
-
#
-
# Replace 'your.smtp.server' with your SMTP server. Normally
-
# your system manager or internet provider supplies a server
-
# for you.
-
#
-
# Then you can send messages.
-
#
-
# msgstr = <<END_OF_MESSAGE
-
# From: Your Name <your@mail.address>
-
# To: Destination Address <someone@example.com>
-
# Subject: test message
-
# Date: Sat, 23 Jun 2001 16:26:43 +0900
-
# Message-Id: <unique.message.id.string@example.com>
-
#
-
# This is a test message.
-
# END_OF_MESSAGE
-
#
-
# require 'net/smtp'
-
# Net::SMTP.start('your.smtp.server', 25) do |smtp|
-
# smtp.send_message msgstr,
-
# 'your@mail.address',
-
# 'his_address@example.com'
-
# end
-
#
-
# === Closing the Session
-
#
-
# You MUST close the SMTP session after sending messages, by calling
-
# the #finish method:
-
#
-
# # using SMTP#finish
-
# smtp = Net::SMTP.start('your.smtp.server', 25)
-
# smtp.send_message msgstr, 'from@address', 'to@address'
-
# smtp.finish
-
#
-
# You can also use the block form of SMTP.start/SMTP#start. This closes
-
# the SMTP session automatically:
-
#
-
# # using block form of SMTP.start
-
# Net::SMTP.start('your.smtp.server', 25) do |smtp|
-
# smtp.send_message msgstr, 'from@address', 'to@address'
-
# end
-
#
-
# I strongly recommend this scheme. This form is simpler and more robust.
-
#
-
# === HELO domain
-
#
-
# In almost all situations, you must provide a third argument
-
# to SMTP.start/SMTP#start. This is the domain name which you are on
-
# (the host to send mail from). It is called the "HELO domain".
-
# The SMTP server will judge whether it should send or reject
-
# the SMTP session by inspecting the HELO domain.
-
#
-
# Net::SMTP.start('your.smtp.server', 25,
-
# 'mail.from.domain') { |smtp| ... }
-
#
-
# === SMTP Authentication
-
#
-
# The Net::SMTP class supports three authentication schemes;
-
# PLAIN, LOGIN and CRAM MD5. (SMTP Authentication: [RFC2554])
-
# To use SMTP authentication, pass extra arguments to
-
# SMTP.start/SMTP#start.
-
#
-
# # PLAIN
-
# Net::SMTP.start('your.smtp.server', 25, 'mail.from.domain',
-
# 'Your Account', 'Your Password', :plain)
-
# # LOGIN
-
# Net::SMTP.start('your.smtp.server', 25, 'mail.from.domain',
-
# 'Your Account', 'Your Password', :login)
-
#
-
# # CRAM MD5
-
# Net::SMTP.start('your.smtp.server', 25, 'mail.from.domain',
-
# 'Your Account', 'Your Password', :cram_md5)
-
#
-
1
class SMTP
-
-
1
Revision = %q$Revision: 31710 $.split[1]
-
-
# The default SMTP port number, 25.
-
1
def SMTP.default_port
-
25
-
end
-
-
# The default mail submission port number, 587.
-
1
def SMTP.default_submission_port
-
587
-
end
-
-
# The default SMTPS port number, 465.
-
1
def SMTP.default_tls_port
-
465
-
end
-
-
1
class << self
-
1
alias default_ssl_port default_tls_port
-
end
-
-
1
def SMTP.default_ssl_context
-
OpenSSL::SSL::SSLContext.new
-
end
-
-
#
-
# Creates a new Net::SMTP object.
-
#
-
# +address+ is the hostname or ip address of your SMTP
-
# server. +port+ is the port to connect to; it defaults to
-
# port 25.
-
#
-
# This method does not open the TCP connection. You can use
-
# SMTP.start instead of SMTP.new if you want to do everything
-
# at once. Otherwise, follow SMTP.new with SMTP#start.
-
#
-
1
def initialize(address, port = nil)
-
@address = address
-
@port = (port || SMTP.default_port)
-
@esmtp = true
-
@capabilities = nil
-
@socket = nil
-
@started = false
-
@open_timeout = 30
-
@read_timeout = 60
-
@error_occured = false
-
@debug_output = nil
-
@tls = false
-
@starttls = false
-
@ssl_context = nil
-
end
-
-
# Provide human-readable stringification of class state.
-
1
def inspect
-
"#<#{self.class} #{@address}:#{@port} started=#{@started}>"
-
end
-
-
#
-
# Set whether to use ESMTP or not. This should be done before
-
# calling #start. Note that if #start is called in ESMTP mode,
-
# and the connection fails due to a ProtocolError, the SMTP
-
# object will automatically switch to plain SMTP mode and
-
# retry (but not vice versa).
-
#
-
1
attr_accessor :esmtp
-
-
# +true+ if the SMTP object uses ESMTP (which it does by default).
-
1
alias :esmtp? :esmtp
-
-
# true if server advertises STARTTLS.
-
# You cannot get valid value before opening SMTP session.
-
1
def capable_starttls?
-
capable?('STARTTLS')
-
end
-
-
1
def capable?(key)
-
return nil unless @capabilities
-
@capabilities[key] ? true : false
-
end
-
1
private :capable?
-
-
# true if server advertises AUTH PLAIN.
-
# You cannot get valid value before opening SMTP session.
-
1
def capable_plain_auth?
-
auth_capable?('PLAIN')
-
end
-
-
# true if server advertises AUTH LOGIN.
-
# You cannot get valid value before opening SMTP session.
-
1
def capable_login_auth?
-
auth_capable?('LOGIN')
-
end
-
-
# true if server advertises AUTH CRAM-MD5.
-
# You cannot get valid value before opening SMTP session.
-
1
def capable_cram_md5_auth?
-
auth_capable?('CRAM-MD5')
-
end
-
-
1
def auth_capable?(type)
-
return nil unless @capabilities
-
return false unless @capabilities['AUTH']
-
@capabilities['AUTH'].include?(type)
-
end
-
1
private :auth_capable?
-
-
# Returns supported authentication methods on this server.
-
# You cannot get valid value before opening SMTP session.
-
1
def capable_auth_types
-
return [] unless @capabilities
-
return [] unless @capabilities['AUTH']
-
@capabilities['AUTH']
-
end
-
-
# true if this object uses SMTP/TLS (SMTPS).
-
1
def tls?
-
@tls
-
end
-
-
1
alias ssl? tls?
-
-
# Enables SMTP/TLS (SMTPS: SMTP over direct TLS connection) for
-
# this object. Must be called before the connection is established
-
# to have any effect. +context+ is a OpenSSL::SSL::SSLContext object.
-
1
def enable_tls(context = SMTP.default_ssl_context)
-
raise 'openssl library not installed' unless defined?(OpenSSL)
-
raise ArgumentError, "SMTPS and STARTTLS is exclusive" if @starttls
-
@tls = true
-
@ssl_context = context
-
end
-
-
1
alias enable_ssl enable_tls
-
-
# Disables SMTP/TLS for this object. Must be called before the
-
# connection is established to have any effect.
-
1
def disable_tls
-
@tls = false
-
@ssl_context = nil
-
end
-
-
1
alias disable_ssl disable_tls
-
-
# Returns truth value if this object uses STARTTLS.
-
# If this object always uses STARTTLS, returns :always.
-
# If this object uses STARTTLS when the server support TLS, returns :auto.
-
1
def starttls?
-
@starttls
-
end
-
-
# true if this object uses STARTTLS.
-
1
def starttls_always?
-
@starttls == :always
-
end
-
-
# true if this object uses STARTTLS when server advertises STARTTLS.
-
1
def starttls_auto?
-
@starttls == :auto
-
end
-
-
# Enables SMTP/TLS (STARTTLS) for this object.
-
# +context+ is a OpenSSL::SSL::SSLContext object.
-
1
def enable_starttls(context = SMTP.default_ssl_context)
-
raise 'openssl library not installed' unless defined?(OpenSSL)
-
raise ArgumentError, "SMTPS and STARTTLS is exclusive" if @tls
-
@starttls = :always
-
@ssl_context = context
-
end
-
-
# Enables SMTP/TLS (STARTTLS) for this object if server accepts.
-
# +context+ is a OpenSSL::SSL::SSLContext object.
-
1
def enable_starttls_auto(context = SMTP.default_ssl_context)
-
raise 'openssl library not installed' unless defined?(OpenSSL)
-
raise ArgumentError, "SMTPS and STARTTLS is exclusive" if @tls
-
@starttls = :auto
-
@ssl_context = context
-
end
-
-
# Disables SMTP/TLS (STARTTLS) for this object. Must be called
-
# before the connection is established to have any effect.
-
1
def disable_starttls
-
@starttls = false
-
@ssl_context = nil
-
end
-
-
# The address of the SMTP server to connect to.
-
1
attr_reader :address
-
-
# The port number of the SMTP server to connect to.
-
1
attr_reader :port
-
-
# Seconds to wait while attempting to open a connection.
-
# If the connection cannot be opened within this time, a
-
# TimeoutError is raised.
-
1
attr_accessor :open_timeout
-
-
# Seconds to wait while reading one block (by one read(2) call).
-
# If the read(2) call does not complete within this time, a
-
# TimeoutError is raised.
-
1
attr_reader :read_timeout
-
-
# Set the number of seconds to wait until timing-out a read(2)
-
# call.
-
1
def read_timeout=(sec)
-
@socket.read_timeout = sec if @socket
-
@read_timeout = sec
-
end
-
-
#
-
# WARNING: This method causes serious security holes.
-
# Use this method for only debugging.
-
#
-
# Set an output stream for debug logging.
-
# You must call this before #start.
-
#
-
# # example
-
# smtp = Net::SMTP.new(addr, port)
-
# smtp.set_debug_output $stderr
-
# smtp.start do |smtp|
-
# ....
-
# end
-
#
-
1
def debug_output=(arg)
-
@debug_output = arg
-
end
-
-
1
alias set_debug_output debug_output=
-
-
#
-
# SMTP session control
-
#
-
-
#
-
# Creates a new Net::SMTP object and connects to the server.
-
#
-
# This method is equivalent to:
-
#
-
# Net::SMTP.new(address, port).start(helo_domain, account, password, authtype)
-
#
-
# === Example
-
#
-
# Net::SMTP.start('your.smtp.server') do |smtp|
-
# smtp.send_message msgstr, 'from@example.com', ['dest@example.com']
-
# end
-
#
-
# === Block Usage
-
#
-
# If called with a block, the newly-opened Net::SMTP object is yielded
-
# to the block, and automatically closed when the block finishes. If called
-
# without a block, the newly-opened Net::SMTP object is returned to
-
# the caller, and it is the caller's responsibility to close it when
-
# finished.
-
#
-
# === Parameters
-
#
-
# +address+ is the hostname or ip address of your smtp server.
-
#
-
# +port+ is the port to connect to; it defaults to port 25.
-
#
-
# +helo+ is the _HELO_ _domain_ provided by the client to the
-
# server (see overview comments); it defaults to 'localhost'.
-
#
-
# The remaining arguments are used for SMTP authentication, if required
-
# or desired. +user+ is the account name; +secret+ is your password
-
# or other authentication token; and +authtype+ is the authentication
-
# type, one of :plain, :login, or :cram_md5. See the discussion of
-
# SMTP Authentication in the overview notes.
-
#
-
# === Errors
-
#
-
# This method may raise:
-
#
-
# * Net::SMTPAuthenticationError
-
# * Net::SMTPServerBusy
-
# * Net::SMTPSyntaxError
-
# * Net::SMTPFatalError
-
# * Net::SMTPUnknownError
-
# * IOError
-
# * TimeoutError
-
#
-
1
def SMTP.start(address, port = nil, helo = 'localhost',
-
user = nil, secret = nil, authtype = nil,
-
&block) # :yield: smtp
-
new(address, port).start(helo, user, secret, authtype, &block)
-
end
-
-
# +true+ if the SMTP session has been started.
-
1
def started?
-
@started
-
end
-
-
#
-
# Opens a TCP connection and starts the SMTP session.
-
#
-
# === Parameters
-
#
-
# +helo+ is the _HELO_ _domain_ that you'll dispatch mails from; see
-
# the discussion in the overview notes.
-
#
-
# If both of +user+ and +secret+ are given, SMTP authentication
-
# will be attempted using the AUTH command. +authtype+ specifies
-
# the type of authentication to attempt; it must be one of
-
# :login, :plain, and :cram_md5. See the notes on SMTP Authentication
-
# in the overview.
-
#
-
# === Block Usage
-
#
-
# When this methods is called with a block, the newly-started SMTP
-
# object is yielded to the block, and automatically closed after
-
# the block call finishes. Otherwise, it is the caller's
-
# responsibility to close the session when finished.
-
#
-
# === Example
-
#
-
# This is very similar to the class method SMTP.start.
-
#
-
# require 'net/smtp'
-
# smtp = Net::SMTP.new('smtp.mail.server', 25)
-
# smtp.start(helo_domain, account, password, authtype) do |smtp|
-
# smtp.send_message msgstr, 'from@example.com', ['dest@example.com']
-
# end
-
#
-
# The primary use of this method (as opposed to SMTP.start)
-
# is probably to set debugging (#set_debug_output) or ESMTP
-
# (#esmtp=), which must be done before the session is
-
# started.
-
#
-
# === Errors
-
#
-
# If session has already been started, an IOError will be raised.
-
#
-
# This method may raise:
-
#
-
# * Net::SMTPAuthenticationError
-
# * Net::SMTPServerBusy
-
# * Net::SMTPSyntaxError
-
# * Net::SMTPFatalError
-
# * Net::SMTPUnknownError
-
# * IOError
-
# * TimeoutError
-
#
-
1
def start(helo = 'localhost',
-
user = nil, secret = nil, authtype = nil) # :yield: smtp
-
if block_given?
-
begin
-
do_start helo, user, secret, authtype
-
return yield(self)
-
ensure
-
do_finish
-
end
-
else
-
do_start helo, user, secret, authtype
-
return self
-
end
-
end
-
-
# Finishes the SMTP session and closes TCP connection.
-
# Raises IOError if not started.
-
1
def finish
-
raise IOError, 'not yet started' unless started?
-
do_finish
-
end
-
-
1
private
-
-
1
def tcp_socket(address, port)
-
TCPSocket.open address, port
-
end
-
-
1
def do_start(helo_domain, user, secret, authtype)
-
raise IOError, 'SMTP session already started' if @started
-
if user or secret
-
check_auth_method(authtype || DEFAULT_AUTH_TYPE)
-
check_auth_args user, secret
-
end
-
s = timeout(@open_timeout) { tcp_socket(@address, @port) }
-
logging "Connection opened: #{@address}:#{@port}"
-
@socket = new_internet_message_io(tls? ? tlsconnect(s) : s)
-
check_response critical { recv_response() }
-
do_helo helo_domain
-
if starttls_always? or (capable_starttls? and starttls_auto?)
-
unless capable_starttls?
-
raise SMTPUnsupportedCommand,
-
"STARTTLS is not supported on this server"
-
end
-
starttls
-
@socket = new_internet_message_io(tlsconnect(s))
-
# helo response may be different after STARTTLS
-
do_helo helo_domain
-
end
-
authenticate user, secret, (authtype || DEFAULT_AUTH_TYPE) if user
-
@started = true
-
ensure
-
unless @started
-
# authentication failed, cancel connection.
-
s.close if s and not s.closed?
-
@socket = nil
-
end
-
end
-
-
1
def ssl_socket(socket, context)
-
OpenSSL::SSL::SSLSocket.new socket, context
-
end
-
-
1
def tlsconnect(s)
-
verified = false
-
s = ssl_socket(s, @ssl_context)
-
logging "TLS connection started"
-
s.sync_close = true
-
s.connect
-
if @ssl_context.verify_mode != OpenSSL::SSL::VERIFY_NONE
-
s.post_connection_check(@address)
-
end
-
verified = true
-
s
-
ensure
-
s.close unless verified
-
end
-
-
1
def new_internet_message_io(s)
-
io = InternetMessageIO.new(s)
-
io.read_timeout = @read_timeout
-
io.debug_output = @debug_output
-
io
-
end
-
-
1
def do_helo(helo_domain)
-
res = @esmtp ? ehlo(helo_domain) : helo(helo_domain)
-
@capabilities = res.capabilities
-
rescue SMTPError
-
if @esmtp
-
@esmtp = false
-
@error_occured = false
-
retry
-
end
-
raise
-
end
-
-
1
def do_finish
-
quit if @socket and not @socket.closed? and not @error_occured
-
ensure
-
@started = false
-
@error_occured = false
-
@socket.close if @socket and not @socket.closed?
-
@socket = nil
-
end
-
-
#
-
# Message Sending
-
#
-
-
1
public
-
-
#
-
# Sends +msgstr+ as a message. Single CR ("\r") and LF ("\n") found
-
# in the +msgstr+, are converted into the CR LF pair. You cannot send a
-
# binary message with this method. +msgstr+ should include both
-
# the message headers and body.
-
#
-
# +from_addr+ is a String representing the source mail address.
-
#
-
# +to_addr+ is a String or Strings or Array of Strings, representing
-
# the destination mail address or addresses.
-
#
-
# === Example
-
#
-
# Net::SMTP.start('smtp.example.com') do |smtp|
-
# smtp.send_message msgstr,
-
# 'from@example.com',
-
# ['dest@example.com', 'dest2@example.com']
-
# end
-
#
-
# === Errors
-
#
-
# This method may raise:
-
#
-
# * Net::SMTPServerBusy
-
# * Net::SMTPSyntaxError
-
# * Net::SMTPFatalError
-
# * Net::SMTPUnknownError
-
# * IOError
-
# * TimeoutError
-
#
-
1
def send_message(msgstr, from_addr, *to_addrs)
-
raise IOError, 'closed session' unless @socket
-
mailfrom from_addr
-
rcptto_list(to_addrs) {data msgstr}
-
end
-
-
1
alias send_mail send_message
-
1
alias sendmail send_message # obsolete
-
-
#
-
# Opens a message writer stream and gives it to the block.
-
# The stream is valid only in the block, and has these methods:
-
#
-
# puts(str = ''):: outputs STR and CR LF.
-
# print(str):: outputs STR.
-
# printf(fmt, *args):: outputs sprintf(fmt,*args).
-
# write(str):: outputs STR and returns the length of written bytes.
-
# <<(str):: outputs STR and returns self.
-
#
-
# If a single CR ("\r") or LF ("\n") is found in the message,
-
# it is converted to the CR LF pair. You cannot send a binary
-
# message with this method.
-
#
-
# === Parameters
-
#
-
# +from_addr+ is a String representing the source mail address.
-
#
-
# +to_addr+ is a String or Strings or Array of Strings, representing
-
# the destination mail address or addresses.
-
#
-
# === Example
-
#
-
# Net::SMTP.start('smtp.example.com', 25) do |smtp|
-
# smtp.open_message_stream('from@example.com', ['dest@example.com']) do |f|
-
# f.puts 'From: from@example.com'
-
# f.puts 'To: dest@example.com'
-
# f.puts 'Subject: test message'
-
# f.puts
-
# f.puts 'This is a test message.'
-
# end
-
# end
-
#
-
# === Errors
-
#
-
# This method may raise:
-
#
-
# * Net::SMTPServerBusy
-
# * Net::SMTPSyntaxError
-
# * Net::SMTPFatalError
-
# * Net::SMTPUnknownError
-
# * IOError
-
# * TimeoutError
-
#
-
1
def open_message_stream(from_addr, *to_addrs, &block) # :yield: stream
-
raise IOError, 'closed session' unless @socket
-
mailfrom from_addr
-
rcptto_list(to_addrs) {data(&block)}
-
end
-
-
1
alias ready open_message_stream # obsolete
-
-
#
-
# Authentication
-
#
-
-
1
public
-
-
1
DEFAULT_AUTH_TYPE = :plain
-
-
1
def authenticate(user, secret, authtype = DEFAULT_AUTH_TYPE)
-
check_auth_method authtype
-
check_auth_args user, secret
-
send auth_method(authtype), user, secret
-
end
-
-
1
def auth_plain(user, secret)
-
check_auth_args user, secret
-
res = critical {
-
get_response('AUTH PLAIN ' + base64_encode("\0#{user}\0#{secret}"))
-
}
-
check_auth_response res
-
res
-
end
-
-
1
def auth_login(user, secret)
-
check_auth_args user, secret
-
res = critical {
-
check_auth_continue get_response('AUTH LOGIN')
-
check_auth_continue get_response(base64_encode(user))
-
get_response(base64_encode(secret))
-
}
-
check_auth_response res
-
res
-
end
-
-
1
def auth_cram_md5(user, secret)
-
check_auth_args user, secret
-
res = critical {
-
res0 = get_response('AUTH CRAM-MD5')
-
check_auth_continue res0
-
crammed = cram_md5_response(secret, res0.cram_md5_challenge)
-
get_response(base64_encode("#{user} #{crammed}"))
-
}
-
check_auth_response res
-
res
-
end
-
-
1
private
-
-
1
def check_auth_method(type)
-
unless respond_to?(auth_method(type), true)
-
raise ArgumentError, "wrong authentication type #{type}"
-
end
-
end
-
-
1
def auth_method(type)
-
"auth_#{type.to_s.downcase}".intern
-
end
-
-
1
def check_auth_args(user, secret, authtype = DEFAULT_AUTH_TYPE)
-
unless user
-
raise ArgumentError, 'SMTP-AUTH requested but missing user name'
-
end
-
unless secret
-
raise ArgumentError, 'SMTP-AUTH requested but missing secret phrase'
-
end
-
end
-
-
1
def base64_encode(str)
-
# expects "str" may not become too long
-
[str].pack('m').gsub(/\s+/, '')
-
end
-
-
1
IMASK = 0x36
-
1
OMASK = 0x5c
-
-
# CRAM-MD5: [RFC2195]
-
1
def cram_md5_response(secret, challenge)
-
tmp = Digest::MD5.digest(cram_secret(secret, IMASK) + challenge)
-
Digest::MD5.hexdigest(cram_secret(secret, OMASK) + tmp)
-
end
-
-
1
CRAM_BUFSIZE = 64
-
-
1
def cram_secret(secret, mask)
-
secret = Digest::MD5.digest(secret) if secret.size > CRAM_BUFSIZE
-
buf = secret.ljust(CRAM_BUFSIZE, "\0")
-
0.upto(buf.size - 1) do |i|
-
buf[i] = (buf[i].ord ^ mask).chr
-
end
-
buf
-
end
-
-
#
-
# SMTP command dispatcher
-
#
-
-
1
public
-
-
1
def starttls
-
getok('STARTTLS')
-
end
-
-
1
def helo(domain)
-
getok("HELO #{domain}")
-
end
-
-
1
def ehlo(domain)
-
getok("EHLO #{domain}")
-
end
-
-
1
def mailfrom(from_addr)
-
if $SAFE > 0
-
raise SecurityError, 'tainted from_addr' if from_addr.tainted?
-
end
-
getok("MAIL FROM:<#{from_addr}>")
-
end
-
-
1
def rcptto_list(to_addrs)
-
raise ArgumentError, 'mail destination not given' if to_addrs.empty?
-
ok_users = []
-
unknown_users = []
-
to_addrs.flatten.each do |addr|
-
begin
-
rcptto addr
-
rescue SMTPAuthenticationError
-
unknown_users << addr.dump
-
else
-
ok_users << addr
-
end
-
end
-
raise ArgumentError, 'mail destination not given' if ok_users.empty?
-
ret = yield
-
unless unknown_users.empty?
-
raise SMTPAuthenticationError, "failed to deliver for #{unknown_users.join(', ')}"
-
end
-
ret
-
end
-
-
1
def rcptto(to_addr)
-
if $SAFE > 0
-
raise SecurityError, 'tainted to_addr' if to_addr.tainted?
-
end
-
getok("RCPT TO:<#{to_addr}>")
-
end
-
-
# This method sends a message.
-
# If +msgstr+ is given, sends it as a message.
-
# If block is given, yield a message writer stream.
-
# You must write message before the block is closed.
-
#
-
# # Example 1 (by string)
-
# smtp.data(<<EndMessage)
-
# From: john@example.com
-
# To: betty@example.com
-
# Subject: I found a bug
-
#
-
# Check vm.c:58879.
-
# EndMessage
-
#
-
# # Example 2 (by block)
-
# smtp.data {|f|
-
# f.puts "From: john@example.com"
-
# f.puts "To: betty@example.com"
-
# f.puts "Subject: I found a bug"
-
# f.puts ""
-
# f.puts "Check vm.c:58879."
-
# }
-
#
-
1
def data(msgstr = nil, &block) #:yield: stream
-
if msgstr and block
-
raise ArgumentError, "message and block are exclusive"
-
end
-
unless msgstr or block
-
raise ArgumentError, "message or block is required"
-
end
-
res = critical {
-
check_continue get_response('DATA')
-
if msgstr
-
@socket.write_message msgstr
-
else
-
@socket.write_message_by_block(&block)
-
end
-
recv_response()
-
}
-
check_response res
-
res
-
end
-
-
1
def quit
-
getok('QUIT')
-
end
-
-
1
private
-
-
1
def getok(reqline)
-
res = critical {
-
@socket.writeline reqline
-
recv_response()
-
}
-
check_response res
-
res
-
end
-
-
1
def get_response(reqline)
-
@socket.writeline reqline
-
recv_response()
-
end
-
-
1
def recv_response
-
buf = ''
-
while true
-
line = @socket.readline
-
buf << line << "\n"
-
break unless line[3,1] == '-' # "210-PIPELINING"
-
end
-
Response.parse(buf)
-
end
-
-
1
def critical(&block)
-
return '200 dummy reply code' if @error_occured
-
begin
-
return yield()
-
rescue Exception
-
@error_occured = true
-
raise
-
end
-
end
-
-
1
def check_response(res)
-
unless res.success?
-
raise res.exception_class, res.message
-
end
-
end
-
-
1
def check_continue(res)
-
unless res.continue?
-
raise SMTPUnknownError, "could not get 3xx (#{res.status})"
-
end
-
end
-
-
1
def check_auth_response(res)
-
unless res.success?
-
raise SMTPAuthenticationError, res.message
-
end
-
end
-
-
1
def check_auth_continue(res)
-
unless res.continue?
-
raise res.exception_class, res.message
-
end
-
end
-
-
# This class represents a response received by the SMTP server. Instances
-
# of this class are created by the SMTP class; they should not be directly
-
# created by the user. For more information on SMTP responses, view
-
# {Section 4.2 of RFC 5321}[http://tools.ietf.org/html/rfc5321#section-4.2]
-
1
class Response
-
# Parses the received response and separates the reply code and the human
-
# readable reply text
-
1
def self.parse(str)
-
new(str[0,3], str)
-
end
-
-
# Creates a new instance of the Response class and sets the status and
-
# string attributes
-
1
def initialize(status, string)
-
@status = status
-
@string = string
-
end
-
-
# The three digit reply code of the SMTP response
-
1
attr_reader :status
-
-
# The human readable reply text of the SMTP response
-
1
attr_reader :string
-
-
# Takes the first digit of the reply code to determine the status type
-
1
def status_type_char
-
@status[0, 1]
-
end
-
-
# Determines whether the response received was a Positive Completion
-
# reply (2xx reply code)
-
1
def success?
-
status_type_char() == '2'
-
end
-
-
# Determines whether the response received was a Positive Intermediate
-
# reply (3xx reply code)
-
1
def continue?
-
status_type_char() == '3'
-
end
-
-
# The first line of the human readable reply text
-
1
def message
-
@string.lines.first
-
end
-
-
# Creates a CRAM-MD5 challenge. You can view more information on CRAM-MD5
-
# on Wikipedia: http://en.wikipedia.org/wiki/CRAM-MD5
-
1
def cram_md5_challenge
-
@string.split(/ /)[1].unpack('m')[0]
-
end
-
-
# Returns a hash of the human readable reply text in the response if it
-
# is multiple lines. It does not return the first line. The key of the
-
# hash is the first word the value of the hash is an array with each word
-
# thereafter being a value in the array
-
1
def capabilities
-
return {} unless @string[3, 1] == '-'
-
h = {}
-
@string.lines.drop(1).each do |line|
-
k, *v = line[4..-1].chomp.split
-
h[k] = v
-
end
-
h
-
end
-
-
# Determines whether there was an error and raies the appropriate error
-
# based on the reply code of the response
-
1
def exception_class
-
case @status
-
when /\A4/ then SMTPServerBusy
-
when /\A50/ then SMTPSyntaxError
-
when /\A53/ then SMTPAuthenticationError
-
when /\A5/ then SMTPFatalError
-
else SMTPUnknownError
-
end
-
end
-
end
-
-
1
def logging(msg)
-
@debug_output << msg + "\n" if @debug_output
-
end
-
-
end # class SMTP
-
-
1
SMTPSession = SMTP
-
-
end
-
=begin
-
= $RCSfile$ -- Loader for all OpenSSL C-space and Ruby-space definitions
-
-
= Info
-
'OpenSSL for Ruby 2' project
-
Copyright (C) 2002 Michal Rokos <m.rokos@sh.cvut.cz>
-
All rights reserved.
-
-
= Licence
-
This program is licenced under the same licence as Ruby.
-
(See the file 'LICENCE'.)
-
-
= Version
-
$Id: openssl.rb 32665 2011-07-25 06:38:44Z nahi $
-
=end
-
-
1
require 'openssl.so'
-
-
1
require 'openssl/bn'
-
1
require 'openssl/cipher'
-
1
require 'openssl/config'
-
1
require 'openssl/digest'
-
1
require 'openssl/ssl-internal'
-
1
require 'openssl/x509-internal'
-
-
#--
-
#
-
# $RCSfile$
-
#
-
# = Ruby-space definitions that completes C-space funcs for BN
-
#
-
# = Info
-
# 'OpenSSL for Ruby 2' project
-
# Copyright (C) 2002 Michal Rokos <m.rokos@sh.cvut.cz>
-
# All rights reserved.
-
#
-
# = Licence
-
# This program is licenced under the same licence as Ruby.
-
# (See the file 'LICENCE'.)
-
#
-
# = Version
-
# $Id: bn.rb 33067 2011-08-25 00:52:10Z drbrain $
-
#
-
#++
-
-
1
module OpenSSL
-
1
class BN
-
1
include Comparable
-
end # BN
-
end # OpenSSL
-
-
##
-
# Add double dispatch to Integer
-
#
-
1
class Integer
-
1
def to_bn
-
OpenSSL::BN::new(self.to_s(16), 16)
-
end
-
end # Integer
-
-
=begin
-
= $RCSfile$ -- Buffering mix-in module.
-
-
= Info
-
'OpenSSL for Ruby 2' project
-
Copyright (C) 2001 GOTOU YUUZOU <gotoyuzo@notwork.org>
-
All rights reserved.
-
-
= Licence
-
This program is licenced under the same licence as Ruby.
-
(See the file 'LICENCE'.)
-
-
= Version
-
$Id: buffering.rb 32012 2011-06-11 14:07:42Z nahi $
-
=end
-
-
##
-
# OpenSSL IO buffering mix-in module.
-
#
-
# This module allows an OpenSSL::SSL::SSLSocket to behave like an IO.
-
-
1
module OpenSSL::Buffering
-
1
include Enumerable
-
-
##
-
# The "sync mode" of the SSLSocket.
-
#
-
# See IO#sync for full details.
-
-
1
attr_accessor :sync
-
-
##
-
# Default size to read from or write to the SSLSocket for buffer operations.
-
-
1
BLOCK_SIZE = 1024*16
-
-
1
def initialize(*args)
-
@eof = false
-
@rbuffer = ""
-
@sync = @io.sync
-
end
-
-
#
-
# for reading.
-
#
-
1
private
-
-
##
-
# Fills the buffer from the underlying SSLSocket
-
-
1
def fill_rbuff
-
begin
-
@rbuffer << self.sysread(BLOCK_SIZE)
-
rescue Errno::EAGAIN
-
retry
-
rescue EOFError
-
@eof = true
-
end
-
end
-
-
##
-
# Consumes +size+ bytes from the buffer
-
-
1
def consume_rbuff(size=nil)
-
if @rbuffer.empty?
-
nil
-
else
-
size = @rbuffer.size unless size
-
ret = @rbuffer[0, size]
-
@rbuffer[0, size] = ""
-
ret
-
end
-
end
-
-
1
public
-
-
##
-
# Reads +size+ bytes from the stream. If +buf+ is provided it must
-
# reference a string which will receive the data.
-
#
-
# See IO#read for full details.
-
-
1
def read(size=nil, buf=nil)
-
if size == 0
-
if buf
-
buf.clear
-
return buf
-
else
-
return ""
-
end
-
end
-
until @eof
-
break if size && size <= @rbuffer.size
-
fill_rbuff
-
end
-
ret = consume_rbuff(size) || ""
-
if buf
-
buf.replace(ret)
-
ret = buf
-
end
-
(size && ret.empty?) ? nil : ret
-
end
-
-
##
-
# Reads at most +maxlen+ bytes from the stream. If +buf+ is provided it
-
# must reference a string which will receive the data.
-
#
-
# See IO#readpartial for full details.
-
-
1
def readpartial(maxlen, buf=nil)
-
if maxlen == 0
-
if buf
-
buf.clear
-
return buf
-
else
-
return ""
-
end
-
end
-
if @rbuffer.empty?
-
begin
-
return sysread(maxlen, buf)
-
rescue Errno::EAGAIN
-
retry
-
end
-
end
-
ret = consume_rbuff(maxlen)
-
if buf
-
buf.replace(ret)
-
ret = buf
-
end
-
raise EOFError if ret.empty?
-
ret
-
end
-
-
##
-
# Reads at most +maxlen+ bytes in the non-blocking manner.
-
#
-
# When no data can be read without blocking it raises
-
# OpenSSL::SSL::SSLError extended by IO::WaitReadable or IO::WaitWritable.
-
#
-
# IO::WaitReadable means SSL needs to read internally so read_nonblock
-
# should be called again when the underlying IO is readable.
-
#
-
# IO::WaitWritable means SSL needs to write internally so read_nonblock
-
# should be called again after the underlying IO is writable.
-
#
-
# OpenSSL::Buffering#read_nonblock needs two rescue clause as follows:
-
#
-
# # emulates blocking read (readpartial).
-
# begin
-
# result = ssl.read_nonblock(maxlen)
-
# rescue IO::WaitReadable
-
# IO.select([io])
-
# retry
-
# rescue IO::WaitWritable
-
# IO.select(nil, [io])
-
# retry
-
# end
-
#
-
# Note that one reason that read_nonblock writes to the underlying IO is
-
# when the peer requests a new TLS/SSL handshake. See openssl the FAQ for
-
# more details. http://www.openssl.org/support/faq.html
-
-
1
def read_nonblock(maxlen, buf=nil)
-
if maxlen == 0
-
if buf
-
buf.clear
-
return buf
-
else
-
return ""
-
end
-
end
-
if @rbuffer.empty?
-
return sysread_nonblock(maxlen, buf)
-
end
-
ret = consume_rbuff(maxlen)
-
if buf
-
buf.replace(ret)
-
ret = buf
-
end
-
raise EOFError if ret.empty?
-
ret
-
end
-
-
##
-
# Reads the next "line+ from the stream. Lines are separated by +eol+. If
-
# +limit+ is provided the result will not be longer than the given number of
-
# bytes.
-
#
-
# +eol+ may be a String or Regexp.
-
#
-
# Unlike IO#gets the line read will not be assigned to +$_+.
-
#
-
# Unlike IO#gets the separator must be provided if a limit is provided.
-
-
1
def gets(eol=$/, limit=nil)
-
idx = @rbuffer.index(eol)
-
until @eof
-
break if idx
-
fill_rbuff
-
idx = @rbuffer.index(eol)
-
end
-
if eol.is_a?(Regexp)
-
size = idx ? idx+$&.size : nil
-
else
-
size = idx ? idx+eol.size : nil
-
end
-
if limit and limit >= 0
-
size = [size, limit].min
-
end
-
consume_rbuff(size)
-
end
-
-
##
-
# Executes the block for every line in the stream where lines are separated
-
# by +eol+.
-
#
-
# See also #gets
-
-
1
def each(eol=$/)
-
while line = self.gets(eol)
-
yield line
-
end
-
end
-
1
alias each_line each
-
-
##
-
# Reads lines from the stream which are separated by +eol+.
-
#
-
# See also #gets
-
-
1
def readlines(eol=$/)
-
ary = []
-
while line = self.gets(eol)
-
ary << line
-
end
-
ary
-
end
-
-
##
-
# Reads a line from the stream which is separated by +eol+.
-
#
-
# Raises EOFError if at end of file.
-
-
1
def readline(eol=$/)
-
raise EOFError if eof?
-
gets(eol)
-
end
-
-
##
-
# Reads one character from the stream. Returns nil if called at end of
-
# file.
-
-
1
def getc
-
read(1)
-
end
-
-
##
-
# Calls the given block once for each byte in the stream.
-
-
1
def each_byte # :yields: byte
-
while c = getc
-
yield(c.ord)
-
end
-
end
-
-
##
-
# Reads a one-character string from the stream. Raises an EOFError at end
-
# of file.
-
-
1
def readchar
-
raise EOFError if eof?
-
getc
-
end
-
-
##
-
# Pushes character +c+ back onto the stream such that a subsequent buffered
-
# character read will return it.
-
#
-
# Unlike IO#getc multiple bytes may be pushed back onto the stream.
-
#
-
# Has no effect on unbuffered reads (such as #sysread).
-
-
1
def ungetc(c)
-
@rbuffer[0,0] = c.chr
-
end
-
-
##
-
# Returns true if the stream is at file which means there is no more data to
-
# be read.
-
-
1
def eof?
-
fill_rbuff if !@eof && @rbuffer.empty?
-
@eof && @rbuffer.empty?
-
end
-
1
alias eof eof?
-
-
#
-
# for writing.
-
#
-
1
private
-
-
##
-
# Writes +s+ to the buffer. When the buffer is full or #sync is true the
-
# buffer is flushed to the underlying socket.
-
-
1
def do_write(s)
-
@wbuffer = "" unless defined? @wbuffer
-
@wbuffer << s
-
@sync ||= false
-
if @sync or @wbuffer.size > BLOCK_SIZE or idx = @wbuffer.rindex($/)
-
remain = idx ? idx + $/.size : @wbuffer.length
-
nwritten = 0
-
while remain > 0
-
str = @wbuffer[nwritten,remain]
-
begin
-
nwrote = syswrite(str)
-
rescue Errno::EAGAIN
-
retry
-
end
-
remain -= nwrote
-
nwritten += nwrote
-
end
-
@wbuffer[0,nwritten] = ""
-
end
-
end
-
-
1
public
-
-
##
-
# Writes +s+ to the stream. If the argument is not a string it will be
-
# converted using String#to_s. Returns the number of bytes written.
-
-
1
def write(s)
-
do_write(s)
-
s.length
-
end
-
-
##
-
# Writes +str+ in the non-blocking manner.
-
#
-
# If there is buffered data, it is flushed first. This may block.
-
#
-
# write_nonblock returns number of bytes written to the SSL connection.
-
#
-
# When no data can be written without blocking it raises
-
# OpenSSL::SSL::SSLError extended by IO::WaitReadable or IO::WaitWritable.
-
#
-
# IO::WaitReadable means SSL needs to read internally so write_nonblock
-
# should be called again after the underlying IO is readable.
-
#
-
# IO::WaitWritable means SSL needs to write internally so write_nonblock
-
# should be called again after underlying IO is writable.
-
#
-
# So OpenSSL::Buffering#write_nonblock needs two rescue clause as follows.
-
#
-
# # emulates blocking write.
-
# begin
-
# result = ssl.write_nonblock(str)
-
# rescue IO::WaitReadable
-
# IO.select([io])
-
# retry
-
# rescue IO::WaitWritable
-
# IO.select(nil, [io])
-
# retry
-
# end
-
#
-
# Note that one reason that write_nonblock reads from the underlying IO
-
# is when the peer requests a new TLS/SSL handshake. See the openssl FAQ
-
# for more details. http://www.openssl.org/support/faq.html
-
-
1
def write_nonblock(s)
-
flush
-
syswrite_nonblock(s)
-
end
-
-
##
-
# Writes +s+ to the stream. +s+ will be converted to a String using
-
# String#to_s.
-
-
1
def << (s)
-
do_write(s)
-
self
-
end
-
-
##
-
# Writes +args+ to the stream along with a record separator.
-
#
-
# See IO#puts for full details.
-
-
1
def puts(*args)
-
s = ""
-
if args.empty?
-
s << "\n"
-
end
-
args.each{|arg|
-
s << arg.to_s
-
if $/ && /\n\z/ !~ s
-
s << "\n"
-
end
-
}
-
do_write(s)
-
nil
-
end
-
-
##
-
# Writes +args+ to the stream.
-
#
-
# See IO#print for full details.
-
-
1
def print(*args)
-
s = ""
-
args.each{ |arg| s << arg.to_s }
-
do_write(s)
-
nil
-
end
-
-
##
-
# Formats and writes to the stream converting parameters under control of
-
# the format string.
-
#
-
# See Kernel#sprintf for format string details.
-
-
1
def printf(s, *args)
-
do_write(s % args)
-
nil
-
end
-
-
##
-
# Flushes buffered data to the SSLSocket.
-
-
1
def flush
-
osync = @sync
-
@sync = true
-
do_write ""
-
return self
-
ensure
-
@sync = osync
-
end
-
-
##
-
# Closes the SSLSocket and flushes any unwritten data.
-
-
1
def close
-
flush rescue nil
-
sysclose
-
end
-
end
-
#--
-
#
-
# $RCSfile$
-
#
-
# = Ruby-space predefined Cipher subclasses
-
#
-
# = Info
-
# 'OpenSSL for Ruby 2' project
-
# Copyright (C) 2002 Michal Rokos <m.rokos@sh.cvut.cz>
-
# All rights reserved.
-
#
-
# = Licence
-
# This program is licenced under the same licence as Ruby.
-
# (See the file 'LICENCE'.)
-
#
-
# = Version
-
# $Id: cipher.rb 33067 2011-08-25 00:52:10Z drbrain $
-
#
-
#++
-
-
1
module OpenSSL
-
1
class Cipher
-
1
%w(AES CAST5 BF DES IDEA RC2 RC4 RC5).each{|name|
-
8
klass = Class.new(Cipher){
-
8
define_method(:initialize){|*args|
-
cipher_name = args.inject(name){|n, arg| "#{n}-#{arg}" }
-
super(cipher_name)
-
}
-
}
-
8
const_set(name, klass)
-
}
-
-
1
%w(128 192 256).each{|keylen|
-
3
klass = Class.new(Cipher){
-
3
define_method(:initialize){|mode|
-
mode ||= "CBC"
-
cipher_name = "AES-#{keylen}-#{mode}"
-
super(cipher_name)
-
}
-
}
-
3
const_set("AES#{keylen}", klass)
-
}
-
-
# Generate, set, and return a random key.
-
# You must call cipher.encrypt or cipher.decrypt before calling this method.
-
1
def random_key
-
str = OpenSSL::Random.random_bytes(self.key_len)
-
self.key = str
-
return str
-
end
-
-
# Generate, set, and return a random iv.
-
# You must call cipher.encrypt or cipher.decrypt before calling this method.
-
1
def random_iv
-
str = OpenSSL::Random.random_bytes(self.iv_len)
-
self.iv = str
-
return str
-
end
-
-
# This class is only provided for backwards compatibility. Use OpenSSL::Cipher in the future.
-
1
class Cipher < Cipher
-
# add warning
-
end
-
end # Cipher
-
end # OpenSSL
-
=begin
-
= Ruby-space definitions that completes C-space funcs for Config
-
-
= Info
-
Copyright (C) 2010 Hiroshi Nakamura <nahi@ruby-lang.org>
-
-
= Licence
-
This program is licenced under the same licence as Ruby.
-
(See the file 'LICENCE'.)
-
-
=end
-
-
1
require 'stringio'
-
-
1
module OpenSSL
-
1
class Config
-
1
include Enumerable
-
-
1
class << self
-
1
def parse(str)
-
c = new()
-
parse_config(StringIO.new(str)).each do |section, hash|
-
c[section] = hash
-
end
-
c
-
end
-
-
1
alias load new
-
-
1
def parse_config(io)
-
begin
-
parse_config_lines(io)
-
rescue ConfigError => e
-
e.message.replace("error in line #{io.lineno}: " + e.message)
-
raise
-
end
-
end
-
-
1
def get_key_string(data, section, key) # :nodoc:
-
if v = data[section] && data[section][key]
-
return v
-
elsif section == 'ENV'
-
if v = ENV[key]
-
return v
-
end
-
end
-
if v = data['default'] && data['default'][key]
-
return v
-
end
-
end
-
-
1
private
-
-
1
def parse_config_lines(io)
-
section = 'default'
-
data = {section => {}}
-
while definition = get_definition(io)
-
definition = clear_comments(definition)
-
next if definition.empty?
-
if definition[0] == ?[
-
if /\[([^\]]*)\]/ =~ definition
-
section = $1.strip
-
data[section] ||= {}
-
else
-
raise ConfigError, "missing close square bracket"
-
end
-
else
-
if /\A([^:\s]*)(?:::([^:\s]*))?\s*=(.*)\z/ =~ definition
-
if $2
-
section = $1
-
key = $2
-
else
-
key = $1
-
end
-
value = unescape_value(data, section, $3)
-
(data[section] ||= {})[key] = value.strip
-
else
-
raise ConfigError, "missing equal sign"
-
end
-
end
-
end
-
data
-
end
-
-
# escape with backslash
-
1
QUOTE_REGEXP_SQ = /\A([^'\\]*(?:\\.[^'\\]*)*)'/
-
# escape with backslash and doubled dq
-
1
QUOTE_REGEXP_DQ = /\A([^"\\]*(?:""[^"\\]*|\\.[^"\\]*)*)"/
-
# escaped char map
-
1
ESCAPE_MAP = {
-
"r" => "\r",
-
"n" => "\n",
-
"b" => "\b",
-
"t" => "\t",
-
}
-
-
1
def unescape_value(data, section, value)
-
scanned = []
-
while m = value.match(/['"\\$]/)
-
scanned << m.pre_match
-
c = m[0]
-
value = m.post_match
-
case c
-
when "'"
-
if m = value.match(QUOTE_REGEXP_SQ)
-
scanned << m[1].gsub(/\\(.)/, '\\1')
-
value = m.post_match
-
else
-
break
-
end
-
when '"'
-
if m = value.match(QUOTE_REGEXP_DQ)
-
scanned << m[1].gsub(/""/, '').gsub(/\\(.)/, '\\1')
-
value = m.post_match
-
else
-
break
-
end
-
when "\\"
-
c = value.slice!(0, 1)
-
scanned << (ESCAPE_MAP[c] || c)
-
when "$"
-
ref, value = extract_reference(value)
-
refsec = section
-
if ref.index('::')
-
refsec, ref = ref.split('::', 2)
-
end
-
if v = get_key_string(data, refsec, ref)
-
scanned << v
-
else
-
raise ConfigError, "variable has no value"
-
end
-
else
-
raise 'must not reaced'
-
end
-
end
-
scanned << value
-
scanned.join
-
end
-
-
1
def extract_reference(value)
-
rest = ''
-
if m = value.match(/\(([^)]*)\)|\{([^}]*)\}/)
-
value = m[1] || m[2]
-
rest = m.post_match
-
elsif [?(, ?{].include?(value[0])
-
raise ConfigError, "no close brace"
-
end
-
if m = value.match(/[a-zA-Z0-9_]*(?:::[a-zA-Z0-9_]*)?/)
-
return m[0], m.post_match + rest
-
else
-
raise
-
end
-
end
-
-
1
def clear_comments(line)
-
# FCOMMENT
-
if m = line.match(/\A([\t\n\f ]*);.*\z/)
-
return m[1]
-
end
-
# COMMENT
-
scanned = []
-
while m = line.match(/[#'"\\]/)
-
scanned << m.pre_match
-
c = m[0]
-
line = m.post_match
-
case c
-
when '#'
-
line = nil
-
break
-
when "'", '"'
-
regexp = (c == "'") ? QUOTE_REGEXP_SQ : QUOTE_REGEXP_DQ
-
scanned << c
-
if m = line.match(regexp)
-
scanned << m[0]
-
line = m.post_match
-
else
-
scanned << line
-
line = nil
-
break
-
end
-
when "\\"
-
scanned << c
-
scanned << line.slice!(0, 1)
-
else
-
raise 'must not reaced'
-
end
-
end
-
scanned << line
-
scanned.join
-
end
-
-
1
def get_definition(io)
-
if line = get_line(io)
-
while /[^\\]\\\z/ =~ line
-
if extra = get_line(io)
-
line += extra
-
else
-
break
-
end
-
end
-
return line.strip
-
end
-
end
-
-
1
def get_line(io)
-
if line = io.gets
-
line.gsub(/[\r\n]*/, '')
-
end
-
end
-
end
-
-
1
def initialize(filename = nil)
-
@data = {}
-
if filename
-
File.open(filename.to_s) do |file|
-
Config.parse_config(file).each do |section, hash|
-
self[section] = hash
-
end
-
end
-
end
-
end
-
-
1
def get_value(section, key)
-
if section.nil?
-
raise TypeError.new('nil not allowed')
-
end
-
section = 'default' if section.empty?
-
get_key_string(section, key)
-
end
-
-
1
def value(arg1, arg2 = nil)
-
warn('Config#value is deprecated; use Config#get_value')
-
if arg2.nil?
-
section, key = 'default', arg1
-
else
-
section, key = arg1, arg2
-
end
-
section ||= 'default'
-
section = 'default' if section.empty?
-
get_key_string(section, key)
-
end
-
-
1
def add_value(section, key, value)
-
check_modify
-
(@data[section] ||= {})[key] = value
-
end
-
-
1
def [](section)
-
@data[section] || {}
-
end
-
-
1
def section(name)
-
warn('Config#section is deprecated; use Config#[]')
-
@data[name] || {}
-
end
-
-
1
def []=(section, pairs)
-
check_modify
-
@data[section] ||= {}
-
pairs.each do |key, value|
-
self.add_value(section, key, value)
-
end
-
end
-
-
1
def sections
-
@data.keys
-
end
-
-
1
def to_s
-
ary = []
-
@data.keys.sort.each do |section|
-
ary << "[ #{section} ]\n"
-
@data[section].keys.each do |key|
-
ary << "#{key}=#{@data[section][key]}\n"
-
end
-
ary << "\n"
-
end
-
ary.join
-
end
-
-
1
def each
-
@data.each do |section, hash|
-
hash.each do |key, value|
-
yield [section, key, value]
-
end
-
end
-
end
-
-
1
def inspect
-
"#<#{self.class.name} sections=#{sections.inspect}>"
-
end
-
-
1
protected
-
-
1
def data
-
@data
-
end
-
-
1
private
-
-
1
def initialize_copy(other)
-
@data = other.data.dup
-
end
-
-
1
def check_modify
-
raise TypeError.new("Insecure: can't modify OpenSSL config") if frozen?
-
end
-
-
1
def get_key_string(section, key)
-
Config.get_key_string(@data, section, key)
-
end
-
end
-
end
-
#--
-
#
-
# $RCSfile$
-
#
-
# = Ruby-space predefined Digest subclasses
-
#
-
# = Info
-
# 'OpenSSL for Ruby 2' project
-
# Copyright (C) 2002 Michal Rokos <m.rokos@sh.cvut.cz>
-
# All rights reserved.
-
#
-
# = Licence
-
# This program is licenced under the same licence as Ruby.
-
# (See the file 'LICENCE'.)
-
#
-
# = Version
-
# $Id: digest.rb 33067 2011-08-25 00:52:10Z drbrain $
-
#
-
#++
-
-
1
module OpenSSL
-
1
class Digest
-
-
1
alg = %w(DSS DSS1 MD2 MD4 MD5 MDC2 RIPEMD160 SHA SHA1)
-
1
if OPENSSL_VERSION_NUMBER > 0x00908000
-
1
alg += %w(SHA224 SHA256 SHA384 SHA512)
-
end
-
-
# Return the +data+ hash computed with +name+ Digest. +name+ is either the
-
# long name or short name of a supported digest algorithm.
-
#
-
# === Examples
-
#
-
# OpenSSL::Digest.digest("SHA256, "abc")
-
#
-
# which is equivalent to:
-
#
-
# OpenSSL::Digest::SHA256.digest("abc")
-
-
1
def self.digest(name, data)
-
super(data, name)
-
end
-
-
1
alg.each{|name|
-
13
klass = Class.new(Digest){
-
13
define_method(:initialize){|*data|
-
if data.length > 1
-
raise ArgumentError,
-
"wrong number of arguments (#{data.length} for 1)"
-
end
-
super(name, data.first)
-
}
-
}
-
26
singleton = (class << klass; self; end)
-
13
singleton.class_eval{
-
13
define_method(:digest){|data| Digest.digest(name, data) }
-
13
define_method(:hexdigest){|data| Digest.hexdigest(name, data) }
-
}
-
13
const_set(name, klass)
-
}
-
-
# This class is only provided for backwards compatibility. Use OpenSSL::Digest in the future.
-
1
class Digest < Digest
-
1
def initialize(*args)
-
# add warning
-
super(*args)
-
end
-
end
-
-
end # Digest
-
end # OpenSSL
-
-
=begin
-
= $RCSfile$ -- Ruby-space definitions that completes C-space funcs for SSL
-
-
= Info
-
'OpenSSL for Ruby 2' project
-
Copyright (C) 2001 GOTOU YUUZOU <gotoyuzo@notwork.org>
-
All rights reserved.
-
-
= Licence
-
This program is licenced under the same licence as Ruby.
-
(See the file 'LICENCE'.)
-
-
= Version
-
$Id: ssl-internal.rb 29189 2010-09-06 01:53:00Z nahi $
-
=end
-
-
1
require "openssl/buffering"
-
1
require "fcntl"
-
-
1
module OpenSSL
-
1
module SSL
-
1
class SSLContext
-
1
DEFAULT_PARAMS = {
-
:ssl_version => "SSLv23",
-
:verify_mode => OpenSSL::SSL::VERIFY_PEER,
-
:ciphers => "ALL:!ADH:!EXPORT:!SSLv2:RC4+RSA:+HIGH:+MEDIUM:+LOW",
-
:options => OpenSSL::SSL::OP_ALL,
-
}
-
-
1
DEFAULT_CERT_STORE = OpenSSL::X509::Store.new
-
1
DEFAULT_CERT_STORE.set_default_paths
-
1
if defined?(OpenSSL::X509::V_FLAG_CRL_CHECK_ALL)
-
1
DEFAULT_CERT_STORE.flags = OpenSSL::X509::V_FLAG_CRL_CHECK_ALL
-
end
-
-
1
def set_params(params={})
-
params = DEFAULT_PARAMS.merge(params)
-
params.each{|name, value| self.__send__("#{name}=", value) }
-
if self.verify_mode != OpenSSL::SSL::VERIFY_NONE
-
unless self.ca_file or self.ca_path or self.cert_store
-
self.cert_store = DEFAULT_CERT_STORE
-
end
-
end
-
return params
-
end
-
end
-
-
1
module SocketForwarder
-
1
def addr
-
to_io.addr
-
end
-
-
1
def peeraddr
-
to_io.peeraddr
-
end
-
-
1
def setsockopt(level, optname, optval)
-
to_io.setsockopt(level, optname, optval)
-
end
-
-
1
def getsockopt(level, optname)
-
to_io.getsockopt(level, optname)
-
end
-
-
1
def fcntl(*args)
-
to_io.fcntl(*args)
-
end
-
-
1
def closed?
-
to_io.closed?
-
end
-
-
1
def do_not_reverse_lookup=(flag)
-
to_io.do_not_reverse_lookup = flag
-
end
-
end
-
-
1
module Nonblock
-
1
def initialize(*args)
-
flag = File::NONBLOCK
-
flag |= @io.fcntl(Fcntl::F_GETFL) if defined?(Fcntl::F_GETFL)
-
@io.fcntl(Fcntl::F_SETFL, flag)
-
super
-
end
-
end
-
-
1
def verify_certificate_identity(cert, hostname)
-
should_verify_common_name = true
-
cert.extensions.each{|ext|
-
next if ext.oid != "subjectAltName"
-
ext.value.split(/,\s+/).each{|general_name|
-
if /\ADNS:(.*)/ =~ general_name
-
should_verify_common_name = false
-
reg = Regexp.escape($1).gsub(/\\\*/, "[^.]+")
-
return true if /\A#{reg}\z/i =~ hostname
-
elsif /\AIP Address:(.*)/ =~ general_name
-
should_verify_common_name = false
-
return true if $1 == hostname
-
end
-
}
-
}
-
if should_verify_common_name
-
cert.subject.to_a.each{|oid, value|
-
if oid == "CN"
-
reg = Regexp.escape(value).gsub(/\\\*/, "[^.]+")
-
return true if /\A#{reg}\z/i =~ hostname
-
end
-
}
-
end
-
return false
-
end
-
1
module_function :verify_certificate_identity
-
-
1
class SSLSocket
-
1
include Buffering
-
1
include SocketForwarder
-
1
include Nonblock
-
-
1
def post_connection_check(hostname)
-
unless OpenSSL::SSL.verify_certificate_identity(peer_cert, hostname)
-
raise SSLError, "hostname does not match the server certificate"
-
end
-
return true
-
end
-
-
1
def session
-
SSL::Session.new(self)
-
rescue SSL::Session::SessionError
-
nil
-
end
-
end
-
-
1
class SSLServer
-
1
include SocketForwarder
-
1
attr_accessor :start_immediately
-
-
1
def initialize(svr, ctx)
-
@svr = svr
-
@ctx = ctx
-
unless ctx.session_id_context
-
session_id = OpenSSL::Digest::MD5.hexdigest($0)
-
@ctx.session_id_context = session_id
-
end
-
@start_immediately = true
-
end
-
-
1
def to_io
-
@svr
-
end
-
-
1
def listen(backlog=5)
-
@svr.listen(backlog)
-
end
-
-
1
def shutdown(how=Socket::SHUT_RDWR)
-
@svr.shutdown(how)
-
end
-
-
1
def accept
-
sock = @svr.accept
-
begin
-
ssl = OpenSSL::SSL::SSLSocket.new(sock, @ctx)
-
ssl.sync_close = true
-
ssl.accept if @start_immediately
-
ssl
-
rescue SSLError => ex
-
sock.close
-
raise ex
-
end
-
end
-
-
1
def close
-
@svr.close
-
end
-
end
-
end
-
end
-
=begin
-
= $RCSfile$ -- Ruby-space definitions that completes C-space funcs for X509 and subclasses
-
-
= Info
-
'OpenSSL for Ruby 2' project
-
Copyright (C) 2002 Michal Rokos <m.rokos@sh.cvut.cz>
-
All rights reserved.
-
-
= Licence
-
This program is licenced under the same licence as Ruby.
-
(See the file 'LICENCE'.)
-
-
= Version
-
$Id: x509-internal.rb 32663 2011-07-25 04:51:26Z nahi $
-
=end
-
-
1
module OpenSSL
-
1
module X509
-
1
class ExtensionFactory
-
1
def create_extension(*arg)
-
if arg.size > 1
-
create_ext(*arg)
-
else
-
send("create_ext_from_"+arg[0].class.name.downcase, arg[0])
-
end
-
end
-
-
1
def create_ext_from_array(ary)
-
raise ExtensionError, "unexpected array form" if ary.size > 3
-
create_ext(ary[0], ary[1], ary[2])
-
end
-
-
1
def create_ext_from_string(str) # "oid = critical, value"
-
oid, value = str.split(/=/, 2)
-
oid.strip!
-
value.strip!
-
create_ext(oid, value)
-
end
-
-
1
def create_ext_from_hash(hash)
-
create_ext(hash["oid"], hash["value"], hash["critical"])
-
end
-
end
-
-
1
class Extension
-
1
def to_s # "oid = critical, value"
-
str = self.oid
-
str << " = "
-
str << "critical, " if self.critical?
-
str << self.value.gsub(/\n/, ", ")
-
end
-
-
1
def to_h # {"oid"=>sn|ln, "value"=>value, "critical"=>true|false}
-
{"oid"=>self.oid,"value"=>self.value,"critical"=>self.critical?}
-
end
-
-
1
def to_a
-
[ self.oid, self.value, self.critical? ]
-
end
-
end
-
-
1
class Name
-
1
module RFC2253DN
-
1
Special = ',=+<>#;'
-
1
HexChar = /[0-9a-fA-F]/
-
1
HexPair = /#{HexChar}#{HexChar}/
-
1
HexString = /#{HexPair}+/
-
1
Pair = /\\(?:[#{Special}]|\\|"|#{HexPair})/
-
1
StringChar = /[^#{Special}\\"]/
-
1
QuoteChar = /[^\\"]/
-
1
AttributeType = /[a-zA-Z][0-9a-zA-Z]*|[0-9]+(?:\.[0-9]+)*/
-
1
AttributeValue = /
-
(?!["#])((?:#{StringChar}|#{Pair})*)|
-
\#(#{HexString})|
-
"((?:#{QuoteChar}|#{Pair})*)"
-
/x
-
1
TypeAndValue = /\A(#{AttributeType})=#{AttributeValue}/
-
-
1
module_function
-
-
1
def expand_pair(str)
-
return nil unless str
-
return str.gsub(Pair){
-
pair = $&
-
case pair.size
-
when 2 then pair[1,1]
-
when 3 then Integer("0x#{pair[1,2]}").chr
-
else raise OpenSSL::X509::NameError, "invalid pair: #{str}"
-
end
-
}
-
end
-
-
1
def expand_hexstring(str)
-
return nil unless str
-
der = str.gsub(HexPair){$&.to_i(16).chr }
-
a1 = OpenSSL::ASN1.decode(der)
-
return a1.value, a1.tag
-
end
-
-
1
def expand_value(str1, str2, str3)
-
value = expand_pair(str1)
-
value, tag = expand_hexstring(str2) unless value
-
value = expand_pair(str3) unless value
-
return value, tag
-
end
-
-
1
def scan(dn)
-
str = dn
-
ary = []
-
while true
-
if md = TypeAndValue.match(str)
-
remain = md.post_match
-
type = md[1]
-
value, tag = expand_value(md[2], md[3], md[4]) rescue nil
-
if value
-
type_and_value = [type, value]
-
type_and_value.push(tag) if tag
-
ary.unshift(type_and_value)
-
if remain.length > 2 && remain[0] == ?,
-
str = remain[1..-1]
-
next
-
elsif remain.length > 2 && remain[0] == ?+
-
raise OpenSSL::X509::NameError,
-
"multi-valued RDN is not supported: #{dn}"
-
elsif remain.empty?
-
break
-
end
-
end
-
end
-
msg_dn = dn[0, dn.length - str.length] + " =>" + str
-
raise OpenSSL::X509::NameError, "malformed RDN: #{msg_dn}"
-
end
-
return ary
-
end
-
end
-
-
1
class << self
-
1
def parse_rfc2253(str, template=OBJECT_TYPE_TEMPLATE)
-
ary = OpenSSL::X509::Name::RFC2253DN.scan(str)
-
self.new(ary, template)
-
end
-
-
1
def parse_openssl(str, template=OBJECT_TYPE_TEMPLATE)
-
ary = str.scan(/\s*([^\/,]+)\s*/).collect{|i| i[0].split("=", 2) }
-
self.new(ary, template)
-
end
-
-
1
alias parse parse_openssl
-
end
-
end
-
-
1
class StoreContext
-
1
def cleanup
-
warn "(#{caller.first}) OpenSSL::X509::StoreContext#cleanup is deprecated with no replacement" if $VERBOSE
-
end
-
end
-
end
-
end
-
#
-
# $originalId: parser.rb,v 1.8 2006/07/06 11:42:07 aamine Exp $
-
#
-
# Copyright (c) 1999-2006 Minero Aoki
-
#
-
# This program is free software.
-
# You can distribute/modify this program under the same terms of ruby.
-
#
-
# As a special exception, when this code is copied by Racc
-
# into a Racc output file, you may use that output file
-
# without restriction.
-
#
-
-
1
unless defined?(NotImplementedError)
-
NotImplementedError = NotImplementError
-
end
-
-
1
module Racc
-
1
class ParseError < StandardError; end
-
end
-
1
unless defined?(::ParseError)
-
1
ParseError = Racc::ParseError
-
end
-
-
1
module Racc
-
-
1
unless defined?(Racc_No_Extentions)
-
1
Racc_No_Extentions = false
-
end
-
-
1
class Parser
-
-
1
Racc_Runtime_Version = '1.4.6'
-
1
Racc_Runtime_Revision = %w$originalRevision: 1.8 $[1]
-
-
1
Racc_Runtime_Core_Version_R = '1.4.6'
-
1
Racc_Runtime_Core_Revision_R = %w$originalRevision: 1.8 $[1]
-
1
begin
-
1
require 'racc/cparse'
-
# Racc_Runtime_Core_Version_C = (defined in extention)
-
1
Racc_Runtime_Core_Revision_C = Racc_Runtime_Core_Id_C.split[2]
-
1
unless new.respond_to?(:_racc_do_parse_c, true)
-
raise LoadError, 'old cparse.so'
-
end
-
1
if Racc_No_Extentions
-
raise LoadError, 'selecting ruby version of racc runtime core'
-
end
-
-
1
Racc_Main_Parsing_Routine = :_racc_do_parse_c
-
1
Racc_YY_Parse_Method = :_racc_yyparse_c
-
1
Racc_Runtime_Core_Version = Racc_Runtime_Core_Version_C
-
1
Racc_Runtime_Core_Revision = Racc_Runtime_Core_Revision_C
-
1
Racc_Runtime_Type = 'c'
-
rescue LoadError
-
Racc_Main_Parsing_Routine = :_racc_do_parse_rb
-
Racc_YY_Parse_Method = :_racc_yyparse_rb
-
Racc_Runtime_Core_Version = Racc_Runtime_Core_Version_R
-
Racc_Runtime_Core_Revision = Racc_Runtime_Core_Revision_R
-
Racc_Runtime_Type = 'ruby'
-
end
-
-
1
def Parser.racc_runtime_type
-
Racc_Runtime_Type
-
end
-
-
1
private
-
-
1
def _racc_setup
-
5
@yydebug = false unless self.class::Racc_debug_parser
-
5
@yydebug = false unless defined?(@yydebug)
-
5
if @yydebug
-
@racc_debug_out = $stderr unless defined?(@racc_debug_out)
-
@racc_debug_out ||= $stderr
-
end
-
5
arg = self.class::Racc_arg
-
5
arg[13] = true if arg.size < 14
-
5
arg
-
end
-
-
1
def _racc_init_sysvars
-
@racc_state = [0]
-
@racc_tstack = []
-
@racc_vstack = []
-
-
@racc_t = nil
-
@racc_val = nil
-
-
@racc_read_next = true
-
-
@racc_user_yyerror = false
-
@racc_error_status = 0
-
end
-
-
###
-
### do_parse
-
###
-
-
class_eval %{
-
def do_parse
-
#{Racc_Main_Parsing_Routine}(_racc_setup(), false)
-
end
-
1
}
-
-
1
def next_token
-
raise NotImplementedError, "#{self.class}\#next_token is not defined"
-
end
-
-
1
def _racc_do_parse_rb(arg, in_debug)
-
action_table, action_check, action_default, action_pointer,
-
_, _, _, _,
-
_, _, token_table, _,
-
_, _, * = arg
-
-
_racc_init_sysvars
-
tok = act = i = nil
-
-
catch(:racc_end_parse) {
-
while true
-
if i = action_pointer[@racc_state[-1]]
-
if @racc_read_next
-
if @racc_t != 0 # not EOF
-
tok, @racc_val = next_token()
-
unless tok # EOF
-
@racc_t = 0
-
else
-
@racc_t = (token_table[tok] or 1) # error token
-
end
-
racc_read_token(@racc_t, tok, @racc_val) if @yydebug
-
@racc_read_next = false
-
end
-
end
-
i += @racc_t
-
unless i >= 0 and
-
act = action_table[i] and
-
action_check[i] == @racc_state[-1]
-
act = action_default[@racc_state[-1]]
-
end
-
else
-
act = action_default[@racc_state[-1]]
-
end
-
while act = _racc_evalact(act, arg)
-
;
-
end
-
end
-
}
-
end
-
-
###
-
### yyparse
-
###
-
-
class_eval %{
-
def yyparse(recv, mid)
-
#{Racc_YY_Parse_Method}(recv, mid, _racc_setup(), true)
-
end
-
1
}
-
-
1
def _racc_yyparse_rb(recv, mid, arg, c_debug)
-
action_table, action_check, action_default, action_pointer,
-
_, _, _, _,
-
_, _, token_table, _,
-
_, _, * = arg
-
-
_racc_init_sysvars
-
act = nil
-
i = nil
-
-
catch(:racc_end_parse) {
-
until i = action_pointer[@racc_state[-1]]
-
while act = _racc_evalact(action_default[@racc_state[-1]], arg)
-
;
-
end
-
end
-
recv.__send__(mid) do |tok, val|
-
unless tok
-
@racc_t = 0
-
else
-
@racc_t = (token_table[tok] or 1) # error token
-
end
-
@racc_val = val
-
@racc_read_next = false
-
-
i += @racc_t
-
unless i >= 0 and
-
act = action_table[i] and
-
action_check[i] == @racc_state[-1]
-
act = action_default[@racc_state[-1]]
-
end
-
while act = _racc_evalact(act, arg)
-
;
-
end
-
-
while not(i = action_pointer[@racc_state[-1]]) or
-
not @racc_read_next or
-
@racc_t == 0 # $
-
unless i and i += @racc_t and
-
i >= 0 and
-
act = action_table[i] and
-
action_check[i] == @racc_state[-1]
-
act = action_default[@racc_state[-1]]
-
end
-
while act = _racc_evalact(act, arg)
-
;
-
end
-
end
-
end
-
}
-
end
-
-
###
-
### common
-
###
-
-
1
def _racc_evalact(act, arg)
-
action_table, action_check, _, action_pointer,
-
_, _, _, _,
-
_, _, _, shift_n, reduce_n,
-
_, _, * = arg
-
nerr = 0 # tmp
-
-
if act > 0 and act < shift_n
-
#
-
# shift
-
#
-
if @racc_error_status > 0
-
@racc_error_status -= 1 unless @racc_t == 1 # error token
-
end
-
@racc_vstack.push @racc_val
-
@racc_state.push act
-
@racc_read_next = true
-
if @yydebug
-
@racc_tstack.push @racc_t
-
racc_shift @racc_t, @racc_tstack, @racc_vstack
-
end
-
-
elsif act < 0 and act > -reduce_n
-
#
-
# reduce
-
#
-
code = catch(:racc_jump) {
-
@racc_state.push _racc_do_reduce(arg, act)
-
false
-
}
-
if code
-
case code
-
when 1 # yyerror
-
@racc_user_yyerror = true # user_yyerror
-
return -reduce_n
-
when 2 # yyaccept
-
return shift_n
-
else
-
raise '[Racc Bug] unknown jump code'
-
end
-
end
-
-
elsif act == shift_n
-
#
-
# accept
-
#
-
racc_accept if @yydebug
-
throw :racc_end_parse, @racc_vstack[0]
-
-
elsif act == -reduce_n
-
#
-
# error
-
#
-
case @racc_error_status
-
when 0
-
unless arg[21] # user_yyerror
-
nerr += 1
-
on_error @racc_t, @racc_val, @racc_vstack
-
end
-
when 3
-
if @racc_t == 0 # is $
-
throw :racc_end_parse, nil
-
end
-
@racc_read_next = true
-
end
-
@racc_user_yyerror = false
-
@racc_error_status = 3
-
while true
-
if i = action_pointer[@racc_state[-1]]
-
i += 1 # error token
-
if i >= 0 and
-
(act = action_table[i]) and
-
action_check[i] == @racc_state[-1]
-
break
-
end
-
end
-
throw :racc_end_parse, nil if @racc_state.size <= 1
-
@racc_state.pop
-
@racc_vstack.pop
-
if @yydebug
-
@racc_tstack.pop
-
racc_e_pop @racc_state, @racc_tstack, @racc_vstack
-
end
-
end
-
return act
-
-
else
-
raise "[Racc Bug] unknown action #{act.inspect}"
-
end
-
-
racc_next_state(@racc_state[-1], @racc_state) if @yydebug
-
-
nil
-
end
-
-
1
def _racc_do_reduce(arg, act)
-
_, _, _, _,
-
goto_table, goto_check, goto_default, goto_pointer,
-
nt_base, reduce_table, _, _,
-
_, use_result, * = arg
-
state = @racc_state
-
vstack = @racc_vstack
-
tstack = @racc_tstack
-
-
i = act * -3
-
len = reduce_table[i]
-
reduce_to = reduce_table[i+1]
-
method_id = reduce_table[i+2]
-
void_array = []
-
-
tmp_t = tstack[-len, len] if @yydebug
-
tmp_v = vstack[-len, len]
-
tstack[-len, len] = void_array if @yydebug
-
vstack[-len, len] = void_array
-
state[-len, len] = void_array
-
-
# tstack must be updated AFTER method call
-
if use_result
-
vstack.push __send__(method_id, tmp_v, vstack, tmp_v[0])
-
else
-
vstack.push __send__(method_id, tmp_v, vstack)
-
end
-
tstack.push reduce_to
-
-
racc_reduce(tmp_t, reduce_to, tstack, vstack) if @yydebug
-
-
k1 = reduce_to - nt_base
-
if i = goto_pointer[k1]
-
i += state[-1]
-
if i >= 0 and (curstate = goto_table[i]) and goto_check[i] == k1
-
return curstate
-
end
-
end
-
goto_default[k1]
-
end
-
-
1
def on_error(t, val, vstack)
-
raise ParseError, sprintf("\nparse error on value %s (%s)",
-
val.inspect, token_to_str(t) || '?')
-
end
-
-
1
def yyerror
-
throw :racc_jump, 1
-
end
-
-
1
def yyaccept
-
throw :racc_jump, 2
-
end
-
-
1
def yyerrok
-
@racc_error_status = 0
-
end
-
-
#
-
# for debugging output
-
#
-
-
1
def racc_read_token(t, tok, val)
-
@racc_debug_out.print 'read '
-
@racc_debug_out.print tok.inspect, '(', racc_token2str(t), ') '
-
@racc_debug_out.puts val.inspect
-
@racc_debug_out.puts
-
end
-
-
1
def racc_shift(tok, tstack, vstack)
-
@racc_debug_out.puts "shift #{racc_token2str tok}"
-
racc_print_stacks tstack, vstack
-
@racc_debug_out.puts
-
end
-
-
1
def racc_reduce(toks, sim, tstack, vstack)
-
out = @racc_debug_out
-
out.print 'reduce '
-
if toks.empty?
-
out.print ' <none>'
-
else
-
toks.each {|t| out.print ' ', racc_token2str(t) }
-
end
-
out.puts " --> #{racc_token2str(sim)}"
-
-
racc_print_stacks tstack, vstack
-
@racc_debug_out.puts
-
end
-
-
1
def racc_accept
-
@racc_debug_out.puts 'accept'
-
@racc_debug_out.puts
-
end
-
-
1
def racc_e_pop(state, tstack, vstack)
-
@racc_debug_out.puts 'error recovering mode: pop token'
-
racc_print_states state
-
racc_print_stacks tstack, vstack
-
@racc_debug_out.puts
-
end
-
-
1
def racc_next_state(curstate, state)
-
@racc_debug_out.puts "goto #{curstate}"
-
racc_print_states state
-
@racc_debug_out.puts
-
end
-
-
1
def racc_print_stacks(t, v)
-
out = @racc_debug_out
-
out.print ' ['
-
t.each_index do |i|
-
out.print ' (', racc_token2str(t[i]), ' ', v[i].inspect, ')'
-
end
-
out.puts ' ]'
-
end
-
-
1
def racc_print_states(s)
-
out = @racc_debug_out
-
out.print ' ['
-
s.each {|st| out.print ' ', st }
-
out.puts ' ]'
-
end
-
-
1
def racc_token2str(tok)
-
self.class::Racc_token_to_s_table[tok] or
-
raise "[Racc Bug] can't convert token #{tok} to string"
-
end
-
-
1
def token_to_str(t)
-
self.class::Racc_token_to_s_table[t]
-
end
-
-
end
-
-
end
-
# Copyright (c) 2003, 2004 Jim Weirich, 2009 Eric Hodel
-
#
-
# Permission is hereby granted, free of charge, to any person obtaining
-
# a copy of this software and associated documentation files (the
-
# "Software"), to deal in the Software without restriction, including
-
# without limitation the rights to use, copy, modify, merge, publish,
-
# distribute, sublicense, and/or sell copies of the Software, and to
-
# permit persons to whom the Software is furnished to do so, subject to
-
# the following conditions:
-
#
-
# The above copyright notice and this permission notice shall be
-
# included in all copies or substantial portions of the Software.
-
#
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-
1
require 'rubygems'
-
1
begin
-
1
gem 'rake'
-
rescue Gem::LoadError
-
end
-
-
1
require 'rake/packagetask'
-
-
##
-
# Create a package based upon a Gem::Specification. Gem packages, as well as
-
# zip files and tar/gzipped packages can be produced by this task.
-
#
-
# In addition to the Rake targets generated by Rake::PackageTask, a
-
# Gem::PackageTask will also generate the following tasks:
-
#
-
# [<b>"<em>package_dir</em>/<em>name</em>-<em>version</em>.gem"</b>]
-
# Create a RubyGems package with the given name and version.
-
#
-
# Example using a Gem::Specification:
-
#
-
# require 'rubygems'
-
# require 'rubygems/package_task'
-
#
-
# spec = Gem::Specification.new do |s|
-
# s.platform = Gem::Platform::RUBY
-
# s.summary = "Ruby based make-like utility."
-
# s.name = 'rake'
-
# s.version = PKG_VERSION
-
# s.requirements << 'none'
-
# s.require_path = 'lib'
-
# s.autorequire = 'rake'
-
# s.files = PKG_FILES
-
# s.description = <<-EOF
-
# Rake is a Make-like program implemented in Ruby. Tasks
-
# and dependencies are specified in standard Ruby syntax.
-
# EOF
-
# end
-
#
-
# Gem::PackageTask.new(spec) do |pkg|
-
# pkg.need_zip = true
-
# pkg.need_tar = true
-
# end
-
-
1
class Gem::PackageTask < Rake::PackageTask
-
-
##
-
# Ruby Gem::Specification containing the metadata for this package. The
-
# name, version and package_files are automatically determined from the
-
# gemspec and don't need to be explicitly provided.
-
-
1
attr_accessor :gem_spec
-
-
##
-
# Create a Gem Package task library. Automatically define the gem if a
-
# block is given. If no block is supplied, then #define needs to be called
-
# to define the task.
-
-
1
def initialize(gem_spec)
-
1
init gem_spec
-
1
yield self if block_given?
-
1
define if block_given?
-
end
-
-
##
-
# Initialization tasks without the "yield self" or define operations.
-
-
1
def init(gem)
-
1
super gem.full_name, :noversion
-
1
@gem_spec = gem
-
1
@package_files += gem_spec.files if gem_spec.files
-
end
-
-
##
-
# Create the Rake tasks and actions specified by this Gem::PackageTask.
-
# (+define+ is automatically called if a block is given to +new+).
-
-
1
def define
-
1
super
-
-
1
task :package => [:gem]
-
-
1
gem_file = File.basename gem_spec.cache_file
-
1
gem_path = File.join package_dir, gem_file
-
1
gem_dir = File.join package_dir, gem_spec.full_name
-
-
1
desc "Build the gem file #{gem_file}"
-
1
task :gem => [gem_path]
-
-
1
trace = Rake.application.options.trace
-
1
Gem.configuration.verbose = trace
-
-
1
file gem_path => [package_dir, gem_dir] + @gem_spec.files do
-
chdir(gem_dir) do
-
when_writing "Creating #{gem_spec.file_name}" do
-
Gem::Builder.new(gem_spec).build
-
verbose trace do
-
mv gem_file, '..'
-
end
-
end
-
end
-
end
-
end
-
-
end
-
-
# = Secure random number generator interface.
-
#
-
# This library is an interface for secure random number generator which is
-
# suitable for generating session key in HTTP cookies, etc.
-
#
-
# It supports following secure random number generators.
-
#
-
# * openssl
-
# * /dev/urandom
-
# * Win32
-
#
-
# == Example
-
#
-
# # random hexadecimal string.
-
# p SecureRandom.hex(10) #=> "52750b30ffbc7de3b362"
-
# p SecureRandom.hex(10) #=> "92b15d6c8dc4beb5f559"
-
# p SecureRandom.hex(11) #=> "6aca1b5c58e4863e6b81b8"
-
# p SecureRandom.hex(12) #=> "94b2fff3e7fd9b9c391a2306"
-
# p SecureRandom.hex(13) #=> "39b290146bea6ce975c37cfc23"
-
# ...
-
#
-
# # random base64 string.
-
# p SecureRandom.base64(10) #=> "EcmTPZwWRAozdA=="
-
# p SecureRandom.base64(10) #=> "9b0nsevdwNuM/w=="
-
# p SecureRandom.base64(10) #=> "KO1nIU+p9DKxGg=="
-
# p SecureRandom.base64(11) #=> "l7XEiFja+8EKEtY="
-
# p SecureRandom.base64(12) #=> "7kJSM/MzBJI+75j8"
-
# p SecureRandom.base64(13) #=> "vKLJ0tXBHqQOuIcSIg=="
-
# ...
-
#
-
# # random binary string.
-
# p SecureRandom.random_bytes(10) #=> "\016\t{\370g\310pbr\301"
-
# p SecureRandom.random_bytes(10) #=> "\323U\030TO\234\357\020\a\337"
-
# ...
-
-
1
begin
-
1
require 'openssl'
-
rescue LoadError
-
end
-
-
1
module SecureRandom
-
# SecureRandom.random_bytes generates a random binary string.
-
#
-
# The argument _n_ specifies the length of the result string.
-
#
-
# If _n_ is not specified, 16 is assumed.
-
# It may be larger in future.
-
#
-
# The result may contain any byte: "\x00" - "\xff".
-
#
-
# p SecureRandom.random_bytes #=> "\xD8\\\xE0\xF4\r\xB2\xFC*WM\xFF\x83\x18\xF45\xB6"
-
# p SecureRandom.random_bytes #=> "m\xDC\xFC/\a\x00Uf\xB2\xB2P\xBD\xFF6S\x97"
-
#
-
# If secure random number generator is not available,
-
# NotImplementedError is raised.
-
1
def self.random_bytes(n=nil)
-
3
n ||= 16
-
-
3
if defined? OpenSSL::Random
-
3
@pid = 0 if !defined?(@pid)
-
3
pid = $$
-
3
if @pid != pid
-
1
now = Time.now
-
1
ary = [now.to_i, now.nsec, @pid, pid]
-
1
OpenSSL::Random.seed(ary.to_s)
-
1
@pid = pid
-
end
-
3
return OpenSSL::Random.random_bytes(n)
-
end
-
-
if !defined?(@has_urandom) || @has_urandom
-
flags = File::RDONLY
-
flags |= File::NONBLOCK if defined? File::NONBLOCK
-
flags |= File::NOCTTY if defined? File::NOCTTY
-
begin
-
File.open("/dev/urandom", flags) {|f|
-
unless f.stat.chardev?
-
raise Errno::ENOENT
-
end
-
@has_urandom = true
-
ret = f.readpartial(n)
-
if ret.length != n
-
raise NotImplementedError, "Unexpected partial read from random device"
-
end
-
return ret
-
}
-
rescue Errno::ENOENT
-
@has_urandom = false
-
end
-
end
-
-
if !defined?(@has_win32)
-
begin
-
require 'Win32API'
-
-
crypt_acquire_context = Win32API.new("advapi32", "CryptAcquireContext", 'PPPII', 'L')
-
@crypt_gen_random = Win32API.new("advapi32", "CryptGenRandom", 'LIP', 'L')
-
-
hProvStr = " " * 4
-
prov_rsa_full = 1
-
crypt_verifycontext = 0xF0000000
-
-
if crypt_acquire_context.call(hProvStr, nil, nil, prov_rsa_full, crypt_verifycontext) == 0
-
raise SystemCallError, "CryptAcquireContext failed: #{lastWin32ErrorMessage}"
-
end
-
@hProv, = hProvStr.unpack('L')
-
-
@has_win32 = true
-
rescue LoadError
-
@has_win32 = false
-
end
-
end
-
if @has_win32
-
bytes = " ".force_encoding("ASCII-8BIT") * n
-
if @crypt_gen_random.call(@hProv, bytes.size, bytes) == 0
-
raise SystemCallError, "CryptGenRandom failed: #{lastWin32ErrorMessage}"
-
end
-
return bytes
-
end
-
-
raise NotImplementedError, "No random device"
-
end
-
-
# SecureRandom.hex generates a random hex string.
-
#
-
# The argument _n_ specifies the length of the random length.
-
# The length of the result string is twice of _n_.
-
#
-
# If _n_ is not specified, 16 is assumed.
-
# It may be larger in future.
-
#
-
# The result may contain 0-9 and a-f.
-
#
-
# p SecureRandom.hex #=> "eb693ec8252cd630102fd0d0fb7c3485"
-
# p SecureRandom.hex #=> "91dc3bfb4de5b11d029d376634589b61"
-
#
-
# If secure random number generator is not available,
-
# NotImplementedError is raised.
-
1
def self.hex(n=nil)
-
3
random_bytes(n).unpack("H*")[0]
-
end
-
-
# SecureRandom.base64 generates a random base64 string.
-
#
-
# The argument _n_ specifies the length of the random length.
-
# The length of the result string is about 4/3 of _n_.
-
#
-
# If _n_ is not specified, 16 is assumed.
-
# It may be larger in future.
-
#
-
# The result may contain A-Z, a-z, 0-9, "+", "/" and "=".
-
#
-
# p SecureRandom.base64 #=> "/2BuBuLf3+WfSKyQbRcc/A=="
-
# p SecureRandom.base64 #=> "6BbW0pxO0YENxn38HMUbcQ=="
-
#
-
# If secure random number generator is not available,
-
# NotImplementedError is raised.
-
#
-
# See RFC 3548 for the definition of base64.
-
1
def self.base64(n=nil)
-
[random_bytes(n)].pack("m*").delete("\n")
-
end
-
-
# SecureRandom.urlsafe_base64 generates a random URL-safe base64 string.
-
#
-
# The argument _n_ specifies the length of the random length.
-
# The length of the result string is about 4/3 of _n_.
-
#
-
# If _n_ is not specified, 16 is assumed.
-
# It may be larger in future.
-
#
-
# The boolean argument _padding_ specifies the padding.
-
# If it is false or nil, padding is not generated.
-
# Otherwise padding is generated.
-
# By default, padding is not generated because "=" may be used as a URL delimiter.
-
#
-
# The result may contain A-Z, a-z, 0-9, "-" and "_".
-
# "=" is also used if _padding_ is true.
-
#
-
# p SecureRandom.urlsafe_base64 #=> "b4GOKm4pOYU_-BOXcrUGDg"
-
# p SecureRandom.urlsafe_base64 #=> "UZLdOkzop70Ddx-IJR0ABg"
-
#
-
# p SecureRandom.urlsafe_base64(nil, true) #=> "i0XQ-7gglIsHGV2_BNPrdQ=="
-
# p SecureRandom.urlsafe_base64(nil, true) #=> "-M8rLhr7JEpJlqFGUMmOxg=="
-
#
-
# If secure random number generator is not available,
-
# NotImplementedError is raised.
-
#
-
# See RFC 3548 for the definition of URL-safe base64.
-
1
def self.urlsafe_base64(n=nil, padding=false)
-
s = [random_bytes(n)].pack("m*")
-
s.delete!("\n")
-
s.tr!("+/", "-_")
-
s.delete!("=") if !padding
-
s
-
end
-
-
# SecureRandom.random_number generates a random number.
-
#
-
# If a positive integer is given as _n_,
-
# SecureRandom.random_number returns an integer:
-
# 0 <= SecureRandom.random_number(n) < n.
-
#
-
# p SecureRandom.random_number(100) #=> 15
-
# p SecureRandom.random_number(100) #=> 88
-
#
-
# If 0 is given or an argument is not given,
-
# SecureRandom.random_number returns a float:
-
# 0.0 <= SecureRandom.random_number() < 1.0.
-
#
-
# p SecureRandom.random_number #=> 0.596506046187744
-
# p SecureRandom.random_number #=> 0.350621695741409
-
#
-
1
def self.random_number(n=0)
-
if 0 < n
-
hex = n.to_s(16)
-
hex = '0' + hex if (hex.length & 1) == 1
-
bin = [hex].pack("H*")
-
mask = bin[0].ord
-
mask |= mask >> 1
-
mask |= mask >> 2
-
mask |= mask >> 4
-
begin
-
rnd = SecureRandom.random_bytes(bin.length)
-
rnd[0] = (rnd[0].ord & mask).chr
-
end until rnd < bin
-
rnd.unpack("H*")[0].hex
-
else
-
# assumption: Float::MANT_DIG <= 64
-
i64 = SecureRandom.random_bytes(8).unpack("Q")[0]
-
Math.ldexp(i64 >> (64-Float::MANT_DIG), -Float::MANT_DIG)
-
end
-
end
-
-
# SecureRandom.uuid generates a v4 random UUID (Universally Unique IDentifier).
-
#
-
# p SecureRandom.uuid #=> "2d931510-d99f-494a-8c67-87feb05e1594"
-
# p SecureRandom.uuid #=> "bad85eb9-0713-4da7-8d36-07a8e4b00eab"
-
# p SecureRandom.uuid #=> "62936e70-1815-439b-bf89-8492855a7e6b"
-
#
-
# The version 4 UUID is purely random (except the version).
-
# It doesn't contain meaningful information such as MAC address, time, etc.
-
#
-
# See RFC 4122 for details of UUID.
-
#
-
1
def self.uuid
-
ary = self.random_bytes(16).unpack("NnnnnN")
-
ary[2] = (ary[2] & 0x0fff) | 0x4000
-
ary[3] = (ary[3] & 0x3fff) | 0x8000
-
"%08x-%04x-%04x-%04x-%04x%08x" % ary
-
end
-
-
# Following code is based on David Garamond's GUID library for Ruby.
-
1
def self.lastWin32ErrorMessage # :nodoc:
-
get_last_error = Win32API.new("kernel32", "GetLastError", '', 'L')
-
format_message = Win32API.new("kernel32", "FormatMessageA", 'LPLLPLPPPPPPPP', 'L')
-
format_message_ignore_inserts = 0x00000200
-
format_message_from_system = 0x00001000
-
-
code = get_last_error.call
-
msg = "\0" * 1024
-
len = format_message.call(format_message_ignore_inserts + format_message_from_system, 0, code, 0, msg, 1024, nil, nil, nil, nil, nil, nil, nil, nil)
-
msg[0, len].tr("\r", '').chomp
-
end
-
end
-
1
require 'socket.so'
-
-
1
class Addrinfo
-
# creates an Addrinfo object from the arguments.
-
#
-
# The arguments are interpreted as similar to self.
-
#
-
# Addrinfo.tcp("0.0.0.0", 4649).family_addrinfo("www.ruby-lang.org", 80)
-
# #=> #<Addrinfo: 221.186.184.68:80 TCP (www.ruby-lang.org:80)>
-
#
-
# Addrinfo.unix("/tmp/sock").family_addrinfo("/tmp/sock2")
-
# #=> #<Addrinfo: /tmp/sock2 SOCK_STREAM>
-
#
-
1
def family_addrinfo(*args)
-
if args.empty?
-
raise ArgumentError, "no address specified"
-
elsif Addrinfo === args.first
-
raise ArgumentError, "too many arguments" if args.length != 1
-
elsif self.ip?
-
raise ArgumentError, "IP address needs host and port but #{args.length} arguments given" if args.length != 2
-
host, port = args
-
Addrinfo.getaddrinfo(host, port, self.pfamily, self.socktype, self.protocol)[0]
-
elsif self.unix?
-
raise ArgumentError, "UNIX socket needs single path argument but #{args.length} arguments given" if args.length != 1
-
path, = args
-
Addrinfo.unix(path)
-
else
-
raise ArgumentError, "unexpected family"
-
end
-
end
-
-
# creates a new Socket connected to the address of +local_addrinfo+.
-
#
-
# If no arguments are given, the address of the socket is not bound.
-
#
-
# If a block is given the created socket is yielded for each address.
-
#
-
1
def connect_internal(local_addrinfo) # :yields: socket
-
sock = Socket.new(self.pfamily, self.socktype, self.protocol)
-
begin
-
sock.ipv6only! if self.ipv6?
-
sock.bind local_addrinfo if local_addrinfo
-
sock.connect(self)
-
if block_given?
-
yield sock
-
else
-
sock
-
end
-
ensure
-
sock.close if !sock.closed? && (block_given? || $!)
-
end
-
end
-
1
private :connect_internal
-
-
# creates a socket connected to the address of self.
-
#
-
# If one or more arguments given as _local_addr_args_,
-
# it is used as the local address of the socket.
-
# _local_addr_args_ is given for family_addrinfo to obtain actual address.
-
#
-
# If no arguments given, the local address of the socket is not bound.
-
#
-
# If a block is given, it is called with the socket and the value of the block is returned.
-
# The socket is returned otherwise.
-
#
-
# Addrinfo.tcp("www.ruby-lang.org", 80).connect_from("0.0.0.0", 4649) {|s|
-
# s.print "GET / HTTP/1.0\r\nHost: www.ruby-lang.org\r\n\r\n"
-
# puts s.read
-
# }
-
#
-
# # Addrinfo object can be taken for the argument.
-
# Addrinfo.tcp("www.ruby-lang.org", 80).connect_from(Addrinfo.tcp("0.0.0.0", 4649)) {|s|
-
# s.print "GET / HTTP/1.0\r\nHost: www.ruby-lang.org\r\n\r\n"
-
# puts s.read
-
# }
-
#
-
1
def connect_from(*local_addr_args, &block)
-
connect_internal(family_addrinfo(*local_addr_args), &block)
-
end
-
-
# creates a socket connected to the address of self.
-
#
-
# If a block is given, it is called with the socket and the value of the block is returned.
-
# The socket is returned otherwise.
-
#
-
# Addrinfo.tcp("www.ruby-lang.org", 80).connect {|s|
-
# s.print "GET / HTTP/1.0\r\nHost: www.ruby-lang.org\r\n\r\n"
-
# puts s.read
-
# }
-
#
-
1
def connect(&block)
-
connect_internal(nil, &block)
-
end
-
-
# creates a socket connected to _remote_addr_args_ and bound to self.
-
#
-
# If a block is given, it is called with the socket and the value of the block is returned.
-
# The socket is returned otherwise.
-
#
-
# Addrinfo.tcp("0.0.0.0", 4649).connect_to("www.ruby-lang.org", 80) {|s|
-
# s.print "GET / HTTP/1.0\r\nHost: www.ruby-lang.org\r\n\r\n"
-
# puts s.read
-
# }
-
#
-
1
def connect_to(*remote_addr_args, &block)
-
remote_addrinfo = family_addrinfo(*remote_addr_args)
-
remote_addrinfo.send(:connect_internal, self, &block)
-
end
-
-
# creates a socket bound to self.
-
#
-
# If a block is given, it is called with the socket and the value of the block is returned.
-
# The socket is returned otherwise.
-
#
-
# Addrinfo.udp("0.0.0.0", 9981).bind {|s|
-
# s.local_address.connect {|s| s.send "hello", 0 }
-
# p s.recv(10) #=> "hello"
-
# }
-
#
-
1
def bind
-
sock = Socket.new(self.pfamily, self.socktype, self.protocol)
-
begin
-
sock.ipv6only! if self.ipv6?
-
sock.setsockopt(:SOCKET, :REUSEADDR, 1)
-
sock.bind(self)
-
if block_given?
-
yield sock
-
else
-
sock
-
end
-
ensure
-
sock.close if !sock.closed? && (block_given? || $!)
-
end
-
end
-
-
# creates a listening socket bound to self.
-
1
def listen(backlog=5)
-
sock = Socket.new(self.pfamily, self.socktype, self.protocol)
-
begin
-
sock.ipv6only! if self.ipv6?
-
sock.setsockopt(:SOCKET, :REUSEADDR, 1)
-
sock.bind(self)
-
sock.listen(backlog)
-
if block_given?
-
yield sock
-
else
-
sock
-
end
-
ensure
-
sock.close if !sock.closed? && (block_given? || $!)
-
end
-
end
-
-
# iterates over the list of Addrinfo objects obtained by Addrinfo.getaddrinfo.
-
#
-
# Addrinfo.foreach(nil, 80) {|x| p x }
-
# #=> #<Addrinfo: 127.0.0.1:80 TCP (:80)>
-
# # #<Addrinfo: 127.0.0.1:80 UDP (:80)>
-
# # #<Addrinfo: [::1]:80 TCP (:80)>
-
# # #<Addrinfo: [::1]:80 UDP (:80)>
-
#
-
1
def self.foreach(nodename, service, family=nil, socktype=nil, protocol=nil, flags=nil, &block)
-
Addrinfo.getaddrinfo(nodename, service, family, socktype, protocol, flags).each(&block)
-
end
-
end
-
-
1
class BasicSocket < IO
-
# Returns an address of the socket suitable for connect in the local machine.
-
#
-
# This method returns _self_.local_address, except following condition.
-
#
-
# - IPv4 unspecified address (0.0.0.0) is replaced by IPv4 loopback address (127.0.0.1).
-
# - IPv6 unspecified address (::) is replaced by IPv6 loopback address (::1).
-
#
-
# If the local address is not suitable for connect, SocketError is raised.
-
# IPv4 and IPv6 address which port is 0 is not suitable for connect.
-
# Unix domain socket which has no path is not suitable for connect.
-
#
-
# Addrinfo.tcp("0.0.0.0", 0).listen {|serv|
-
# p serv.connect_address #=> #<Addrinfo: 127.0.0.1:53660 TCP>
-
# serv.connect_address.connect {|c|
-
# s, _ = serv.accept
-
# p [c, s] #=> [#<Socket:fd 4>, #<Socket:fd 6>]
-
# }
-
# }
-
#
-
1
def connect_address
-
addr = local_address
-
afamily = addr.afamily
-
if afamily == Socket::AF_INET
-
raise SocketError, "unbound IPv4 socket" if addr.ip_port == 0
-
if addr.ip_address == "0.0.0.0"
-
addr = Addrinfo.new(["AF_INET", addr.ip_port, nil, "127.0.0.1"], addr.pfamily, addr.socktype, addr.protocol)
-
end
-
elsif defined?(Socket::AF_INET6) && afamily == Socket::AF_INET6
-
raise SocketError, "unbound IPv6 socket" if addr.ip_port == 0
-
if addr.ip_address == "::"
-
addr = Addrinfo.new(["AF_INET6", addr.ip_port, nil, "::1"], addr.pfamily, addr.socktype, addr.protocol)
-
elsif addr.ip_address == "0.0.0.0" # MacOS X 10.4 returns "a.b.c.d" for IPv4-mapped IPv6 address.
-
addr = Addrinfo.new(["AF_INET6", addr.ip_port, nil, "::1"], addr.pfamily, addr.socktype, addr.protocol)
-
elsif addr.ip_address == "::ffff:0.0.0.0" # MacOS X 10.6 returns "::ffff:a.b.c.d" for IPv4-mapped IPv6 address.
-
addr = Addrinfo.new(["AF_INET6", addr.ip_port, nil, "::1"], addr.pfamily, addr.socktype, addr.protocol)
-
end
-
elsif defined?(Socket::AF_UNIX) && afamily == Socket::AF_UNIX
-
raise SocketError, "unbound Unix socket" if addr.unix_path == ""
-
end
-
addr
-
end
-
end
-
-
1
class Socket < BasicSocket
-
# enable the socket option IPV6_V6ONLY if IPV6_V6ONLY is available.
-
1
def ipv6only!
-
if defined? Socket::IPV6_V6ONLY
-
self.setsockopt(:IPV6, :V6ONLY, 1)
-
end
-
end
-
-
# creates a new socket object connected to host:port using TCP/IP.
-
#
-
# If local_host:local_port is given,
-
# the socket is bound to it.
-
#
-
# If a block is given, the block is called with the socket.
-
# The value of the block is returned.
-
# The socket is closed when this method returns.
-
#
-
# If no block is given, the socket is returned.
-
#
-
# Socket.tcp("www.ruby-lang.org", 80) {|sock|
-
# sock.print "GET / HTTP/1.0\r\nHost: www.ruby-lang.org\r\n\r\n"
-
# sock.close_write
-
# puts sock.read
-
# }
-
#
-
1
def self.tcp(host, port, local_host=nil, local_port=nil) # :yield: socket
-
last_error = nil
-
ret = nil
-
-
local_addr_list = nil
-
if local_host != nil || local_port != nil
-
local_addr_list = Addrinfo.getaddrinfo(local_host, local_port, nil, :STREAM, nil)
-
end
-
-
Addrinfo.foreach(host, port, nil, :STREAM) {|ai|
-
if local_addr_list
-
local_addr = local_addr_list.find {|local_ai| local_ai.afamily == ai.afamily }
-
next if !local_addr
-
else
-
local_addr = nil
-
end
-
begin
-
sock = local_addr ? ai.connect_from(local_addr) : ai.connect
-
rescue SystemCallError
-
last_error = $!
-
next
-
end
-
ret = sock
-
break
-
}
-
if !ret
-
if last_error
-
raise last_error
-
else
-
raise SocketError, "no appropriate local address"
-
end
-
end
-
if block_given?
-
begin
-
yield ret
-
ensure
-
ret.close if !ret.closed?
-
end
-
else
-
ret
-
end
-
end
-
-
# :stopdoc:
-
1
def self.ip_sockets_port0(ai_list, reuseaddr)
-
begin
-
sockets = []
-
port = nil
-
ai_list.each {|ai|
-
begin
-
s = Socket.new(ai.pfamily, ai.socktype, ai.protocol)
-
rescue SystemCallError
-
next
-
end
-
sockets << s
-
s.ipv6only! if ai.ipv6?
-
if reuseaddr
-
s.setsockopt(:SOCKET, :REUSEADDR, 1)
-
end
-
if !port
-
s.bind(ai)
-
port = s.local_address.ip_port
-
else
-
s.bind(ai.family_addrinfo(ai.ip_address, port))
-
end
-
}
-
rescue Errno::EADDRINUSE
-
sockets.each {|s|
-
s.close
-
}
-
retry
-
end
-
sockets
-
ensure
-
sockets.each {|s| s.close if !s.closed? } if $!
-
end
-
1
class << self
-
1
private :ip_sockets_port0
-
end
-
-
1
def self.tcp_server_sockets_port0(host)
-
ai_list = Addrinfo.getaddrinfo(host, 0, nil, :STREAM, nil, Socket::AI_PASSIVE)
-
sockets = ip_sockets_port0(ai_list, true)
-
sockets.each {|s|
-
s.listen(5)
-
}
-
sockets
-
ensure
-
sockets.each {|s| s.close if !s.closed? } if $! && sockets
-
end
-
1
class << self
-
1
private :tcp_server_sockets_port0
-
end
-
# :startdoc:
-
-
# creates TCP/IP server sockets for _host_ and _port_.
-
# _host_ is optional.
-
#
-
# If no block given,
-
# it returns an array of listening sockets.
-
#
-
# If a block is given, the block is called with the sockets.
-
# The value of the block is returned.
-
# The socket is closed when this method returns.
-
#
-
# If _port_ is 0, actual port number is choosen dynamically.
-
# However all sockets in the result has same port number.
-
#
-
# # tcp_server_sockets returns two sockets.
-
# sockets = Socket.tcp_server_sockets(1296)
-
# p sockets #=> [#<Socket:fd 3>, #<Socket:fd 4>]
-
#
-
# # The sockets contains IPv6 and IPv4 sockets.
-
# sockets.each {|s| p s.local_address }
-
# #=> #<Addrinfo: [::]:1296 TCP>
-
# # #<Addrinfo: 0.0.0.0:1296 TCP>
-
#
-
# # IPv6 and IPv4 socket has same port number, 53114, even if it is choosen dynamically.
-
# sockets = Socket.tcp_server_sockets(0)
-
# sockets.each {|s| p s.local_address }
-
# #=> #<Addrinfo: [::]:53114 TCP>
-
# # #<Addrinfo: 0.0.0.0:53114 TCP>
-
#
-
# # The block is called with the sockets.
-
# Socket.tcp_server_sockets(0) {|sockets|
-
# p sockets #=> [#<Socket:fd 3>, #<Socket:fd 4>]
-
# }
-
#
-
1
def self.tcp_server_sockets(host=nil, port)
-
if port == 0
-
sockets = tcp_server_sockets_port0(host)
-
else
-
begin
-
last_error = nil
-
sockets = []
-
Addrinfo.foreach(host, port, nil, :STREAM, nil, Socket::AI_PASSIVE) {|ai|
-
begin
-
s = ai.listen
-
rescue SystemCallError
-
last_error = $!
-
next
-
end
-
sockets << s
-
}
-
if sockets.empty?
-
raise last_error
-
end
-
ensure
-
sockets.each {|s| s.close if !s.closed? } if $!
-
end
-
end
-
if block_given?
-
begin
-
yield sockets
-
ensure
-
sockets.each {|s| s.close if !s.closed? }
-
end
-
else
-
sockets
-
end
-
end
-
-
# yield socket and client address for each a connection accepted via given sockets.
-
#
-
# The arguments are a list of sockets.
-
# The individual argument should be a socket or an array of sockets.
-
#
-
# This method yields the block sequentially.
-
# It means that the next connection is not accepted until the block returns.
-
# So concurrent mechanism, thread for example, should be used to service multiple clients at a time.
-
#
-
1
def self.accept_loop(*sockets) # :yield: socket, client_addrinfo
-
sockets.flatten!(1)
-
if sockets.empty?
-
raise ArgumentError, "no sockets"
-
end
-
loop {
-
readable, _, _ = IO.select(sockets)
-
readable.each {|r|
-
begin
-
sock, addr = r.accept_nonblock
-
rescue IO::WaitReadable
-
next
-
end
-
yield sock, addr
-
}
-
}
-
end
-
-
# creates a TCP/IP server on _port_ and calls the block for each connection accepted.
-
# The block is called with a socket and a client_address as an Addrinfo object.
-
#
-
# If _host_ is specified, it is used with _port_ to determine the server addresses.
-
#
-
# The socket is *not* closed when the block returns.
-
# So application should close it explicitly.
-
#
-
# This method calls the block sequentially.
-
# It means that the next connection is not accepted until the block returns.
-
# So concurrent mechanism, thread for example, should be used to service multiple clients at a time.
-
#
-
# Note that Addrinfo.getaddrinfo is used to determine the server socket addresses.
-
# When Addrinfo.getaddrinfo returns two or more addresses,
-
# IPv4 and IPv6 address for example,
-
# all of them are used.
-
# Socket.tcp_server_loop succeeds if one socket can be used at least.
-
#
-
# # Sequential echo server.
-
# # It services only one client at a time.
-
# Socket.tcp_server_loop(16807) {|sock, client_addrinfo|
-
# begin
-
# IO.copy_stream(sock, sock)
-
# ensure
-
# sock.close
-
# end
-
# }
-
#
-
# # Threaded echo server
-
# # It services multiple clients at a time.
-
# # Note that it may accept connections too much.
-
# Socket.tcp_server_loop(16807) {|sock, client_addrinfo|
-
# Thread.new {
-
# begin
-
# IO.copy_stream(sock, sock)
-
# ensure
-
# sock.close
-
# end
-
# }
-
# }
-
#
-
1
def self.tcp_server_loop(host=nil, port, &b) # :yield: socket, client_addrinfo
-
tcp_server_sockets(host, port) {|sockets|
-
accept_loop(sockets, &b)
-
}
-
end
-
-
# :call-seq:
-
# Socket.udp_server_sockets([host, ] port)
-
#
-
# Creates UDP/IP sockets for a UDP server.
-
#
-
# If no block given, it returns an array of sockets.
-
#
-
# If a block is given, the block is called with the sockets.
-
# The value of the block is returned.
-
# The sockets are closed when this method returns.
-
#
-
# If _port_ is zero, some port is choosen.
-
# But the choosen port is used for the all sockets.
-
#
-
# # UDP/IP echo server
-
# Socket.udp_server_sockets(0) {|sockets|
-
# p sockets.first.local_address.ip_port #=> 32963
-
# Socket.udp_server_loop_on(sockets) {|msg, msg_src|
-
# msg_src.reply msg
-
# }
-
# }
-
#
-
1
def self.udp_server_sockets(host=nil, port)
-
last_error = nil
-
sockets = []
-
-
ipv6_recvpktinfo = nil
-
if defined? Socket::AncillaryData
-
if defined? Socket::IPV6_RECVPKTINFO # RFC 3542
-
ipv6_recvpktinfo = Socket::IPV6_RECVPKTINFO
-
elsif defined? Socket::IPV6_PKTINFO # RFC 2292
-
ipv6_recvpktinfo = Socket::IPV6_PKTINFO
-
end
-
end
-
-
local_addrs = Socket.ip_address_list
-
-
ip_list = []
-
Addrinfo.foreach(host, port, nil, :DGRAM, nil, Socket::AI_PASSIVE) {|ai|
-
if ai.ipv4? && ai.ip_address == "0.0.0.0"
-
local_addrs.each {|a|
-
next if !a.ipv4?
-
ip_list << Addrinfo.new(a.to_sockaddr, :INET, :DGRAM, 0);
-
}
-
elsif ai.ipv6? && ai.ip_address == "::" && !ipv6_recvpktinfo
-
local_addrs.each {|a|
-
next if !a.ipv6?
-
ip_list << Addrinfo.new(a.to_sockaddr, :INET6, :DGRAM, 0);
-
}
-
else
-
ip_list << ai
-
end
-
}
-
-
if port == 0
-
sockets = ip_sockets_port0(ip_list, false)
-
else
-
ip_list.each {|ip|
-
ai = Addrinfo.udp(ip.ip_address, port)
-
begin
-
s = ai.bind
-
rescue SystemCallError
-
last_error = $!
-
next
-
end
-
sockets << s
-
}
-
if sockets.empty?
-
raise last_error
-
end
-
end
-
-
sockets.each {|s|
-
ai = s.local_address
-
if ipv6_recvpktinfo && ai.ipv6? && ai.ip_address == "::"
-
s.setsockopt(:IPV6, ipv6_recvpktinfo, 1)
-
end
-
}
-
-
if block_given?
-
begin
-
yield sockets
-
ensure
-
sockets.each {|s| s.close if !s.closed? } if sockets
-
end
-
else
-
sockets
-
end
-
end
-
-
# :call-seq:
-
# Socket.udp_server_recv(sockets) {|msg, msg_src| ... }
-
#
-
# Receive UDP/IP packets from the given _sockets_.
-
# For each packet received, the block is called.
-
#
-
# The block receives _msg_ and _msg_src_.
-
# _msg_ is a string which is the payload of the received packet.
-
# _msg_src_ is a Socket::UDPSource object which is used for reply.
-
#
-
# Socket.udp_server_loop can be implemented using this method as follows.
-
#
-
# udp_server_sockets(host, port) {|sockets|
-
# loop {
-
# readable, _, _ = IO.select(sockets)
-
# udp_server_recv(readable) {|msg, msg_src| ... }
-
# }
-
# }
-
#
-
1
def self.udp_server_recv(sockets)
-
sockets.each {|r|
-
begin
-
msg, sender_addrinfo, _, *controls = r.recvmsg_nonblock
-
rescue IO::WaitReadable
-
next
-
end
-
ai = r.local_address
-
if ai.ipv6? and pktinfo = controls.find {|c| c.cmsg_is?(:IPV6, :PKTINFO) }
-
ai = Addrinfo.udp(pktinfo.ipv6_pktinfo_addr.ip_address, ai.ip_port)
-
yield msg, UDPSource.new(sender_addrinfo, ai) {|reply_msg|
-
r.sendmsg reply_msg, 0, sender_addrinfo, pktinfo
-
}
-
else
-
yield msg, UDPSource.new(sender_addrinfo, ai) {|reply_msg|
-
r.send reply_msg, 0, sender_addrinfo
-
}
-
end
-
}
-
end
-
-
# :call-seq:
-
# Socket.udp_server_loop_on(sockets) {|msg, msg_src| ... }
-
#
-
# Run UDP/IP server loop on the given sockets.
-
#
-
# The return value of Socket.udp_server_sockets is appropriate for the argument.
-
#
-
# It calls the block for each message received.
-
#
-
1
def self.udp_server_loop_on(sockets, &b) # :yield: msg, msg_src
-
loop {
-
readable, _, _ = IO.select(sockets)
-
udp_server_recv(readable, &b)
-
}
-
end
-
-
# :call-seq:
-
# Socket.udp_server_loop(port) {|msg, msg_src| ... }
-
# Socket.udp_server_loop(host, port) {|msg, msg_src| ... }
-
#
-
# creates a UDP/IP server on _port_ and calls the block for each message arrived.
-
# The block is called with the message and its source information.
-
#
-
# This method allocates sockets internally using _port_.
-
# If _host_ is specified, it is used conjunction with _port_ to determine the server addresses.
-
#
-
# The _msg_ is a string.
-
#
-
# The _msg_src_ is a Socket::UDPSource object.
-
# It is used for reply.
-
#
-
# # UDP/IP echo server.
-
# Socket.udp_server_loop(9261) {|msg, msg_src|
-
# msg_src.reply msg
-
# }
-
#
-
1
def self.udp_server_loop(host=nil, port, &b) # :yield: message, message_source
-
udp_server_sockets(host, port) {|sockets|
-
udp_server_loop_on(sockets, &b)
-
}
-
end
-
-
# UDP/IP address information used by Socket.udp_server_loop.
-
1
class UDPSource
-
# +remote_adress+ is an Addrinfo object.
-
#
-
# +local_adress+ is an Addrinfo object.
-
#
-
# +reply_proc+ is a Proc used to send reply back to the source.
-
1
def initialize(remote_address, local_address, &reply_proc)
-
@remote_address = remote_address
-
@local_address = local_address
-
@reply_proc = reply_proc
-
end
-
-
# Address of the source
-
1
attr_reader :remote_address
-
-
# Local address
-
1
attr_reader :local_address
-
-
1
def inspect # :nodoc:
-
"\#<#{self.class}: #{@remote_address.inspect_sockaddr} to #{@local_address.inspect_sockaddr}>"
-
end
-
-
# Sends the String +msg+ to the source
-
1
def reply(msg)
-
@reply_proc.call msg
-
end
-
end
-
-
# creates a new socket connected to path using UNIX socket socket.
-
#
-
# If a block is given, the block is called with the socket.
-
# The value of the block is returned.
-
# The socket is closed when this method returns.
-
#
-
# If no block is given, the socket is returned.
-
#
-
# # talk to /tmp/sock socket.
-
# Socket.unix("/tmp/sock") {|sock|
-
# t = Thread.new { IO.copy_stream(sock, STDOUT) }
-
# IO.copy_stream(STDIN, sock)
-
# t.join
-
# }
-
#
-
1
def self.unix(path) # :yield: socket
-
addr = Addrinfo.unix(path)
-
sock = addr.connect
-
if block_given?
-
begin
-
yield sock
-
ensure
-
sock.close if !sock.closed?
-
end
-
else
-
sock
-
end
-
end
-
-
# creates a UNIX server socket on _path_
-
#
-
# If no block given, it returns a listening socket.
-
#
-
# If a block is given, it is called with the socket and the block value is returned.
-
# When the block exits, the socket is closed and the socket file is removed.
-
#
-
# socket = Socket.unix_server_socket("/tmp/s")
-
# p socket #=> #<Socket:fd 3>
-
# p socket.local_address #=> #<Addrinfo: /tmp/s SOCK_STREAM>
-
#
-
# Socket.unix_server_socket("/tmp/sock") {|s|
-
# p s #=> #<Socket:fd 3>
-
# p s.local_address #=> # #<Addrinfo: /tmp/sock SOCK_STREAM>
-
# }
-
#
-
1
def self.unix_server_socket(path)
-
begin
-
st = File.lstat(path)
-
rescue Errno::ENOENT
-
end
-
if st && st.socket? && st.owned?
-
File.unlink path
-
end
-
s = Addrinfo.unix(path).listen
-
if block_given?
-
begin
-
yield s
-
ensure
-
s.close if !s.closed?
-
File.unlink path
-
end
-
else
-
s
-
end
-
end
-
-
# creates a UNIX socket server on _path_.
-
# It calls the block for each socket accepted.
-
#
-
# If _host_ is specified, it is used with _port_ to determine the server ports.
-
#
-
# The socket is *not* closed when the block returns.
-
# So application should close it.
-
#
-
# This method deletes the socket file pointed by _path_ at first if
-
# the file is a socket file and it is owned by the user of the application.
-
# This is safe only if the directory of _path_ is not changed by a malicious user.
-
# So don't use /tmp/malicious-users-directory/socket.
-
# Note that /tmp/socket and /tmp/your-private-directory/socket is safe assuming that /tmp has sticky bit.
-
#
-
# # Sequential echo server.
-
# # It services only one client at a time.
-
# Socket.unix_server_loop("/tmp/sock") {|sock, client_addrinfo|
-
# begin
-
# IO.copy_stream(sock, sock)
-
# ensure
-
# sock.close
-
# end
-
# }
-
#
-
1
def self.unix_server_loop(path, &b) # :yield: socket, client_addrinfo
-
unix_server_socket(path) {|serv|
-
accept_loop(serv, &b)
-
}
-
end
-
-
end
-
-
#
-
# tempfile - manipulates temporary files
-
#
-
# $Id: tempfile.rb 33089 2011-08-26 23:54:49Z drbrain $
-
#
-
-
1
require 'delegate'
-
1
require 'tmpdir'
-
1
require 'thread'
-
-
# A utility class for managing temporary files. When you create a Tempfile
-
# object, it will create a temporary file with a unique filename. A Tempfile
-
# objects behaves just like a File object, and you can perform all the usual
-
# file operations on it: reading data, writing data, changing its permissions,
-
# etc. So although this class does not explicitly document all instance methods
-
# supported by File, you can in fact call any File instance method on a
-
# Tempfile object.
-
#
-
# == Synopsis
-
#
-
# require 'tempfile'
-
#
-
# file = Tempfile.new('foo')
-
# file.path # => A unique filename in the OS's temp directory,
-
# # e.g.: "/tmp/foo.24722.0"
-
# # This filename contains 'foo' in its basename.
-
# file.write("hello world")
-
# file.rewind
-
# file.read # => "hello world"
-
# file.close
-
# file.unlink # deletes the temp file
-
#
-
# == Good practices
-
#
-
# === Explicit close
-
#
-
# When a Tempfile object is garbage collected, or when the Ruby interpreter
-
# exits, its associated temporary file is automatically deleted. This means
-
# that's it's unnecessary to explicitly delete a Tempfile after use, though
-
# it's good practice to do so: not explicitly deleting unused Tempfiles can
-
# potentially leave behind large amounts of tempfiles on the filesystem
-
# until they're garbage collected. The existence of these temp files can make
-
# it harder to determine a new Tempfile filename.
-
#
-
# Therefore, one should always call #unlink or close in an ensure block, like
-
# this:
-
#
-
# file = Tempfile.new('foo')
-
# begin
-
# ...do something with file...
-
# ensure
-
# file.close
-
# file.unlink # deletes the temp file
-
# end
-
#
-
# === Unlink after creation
-
#
-
# On POSIX systems, it's possible to unlink a file right after creating it,
-
# and before closing it. This removes the filesystem entry without closing
-
# the file handle, so it ensures that only the processes that already had
-
# the file handle open can access the file's contents. It's strongly
-
# recommended that you do this if you do not want any other processes to
-
# be able to read from or write to the Tempfile, and you do not need to
-
# know the Tempfile's filename either.
-
#
-
# For example, a practical use case for unlink-after-creation would be this:
-
# you need a large byte buffer that's too large to comfortably fit in RAM,
-
# e.g. when you're writing a web server and you want to buffer the client's
-
# file upload data.
-
#
-
# Please refer to #unlink for more information and a code example.
-
#
-
# == Minor notes
-
#
-
# Tempfile's filename picking method is both thread-safe and inter-process-safe:
-
# it guarantees that no other threads or processes will pick the same filename.
-
#
-
# Tempfile itself however may not be entirely thread-safe. If you access the
-
# same Tempfile object from multiple threads then you should protect it with a
-
# mutex.
-
1
class Tempfile < DelegateClass(File)
-
1
MAX_TRY = 10 # :nodoc:
-
1
include Dir::Tmpname
-
-
# call-seq:
-
# new(basename, [tmpdir = Dir.tmpdir], [options])
-
#
-
# Creates a temporary file with permissions 0600 (= only readable and
-
# writable by the owner) and opens it with mode "w+".
-
#
-
# The +basename+ parameter is used to determine the name of the
-
# temporary file. You can either pass a String or an Array with
-
# 2 String elements. In the former form, the temporary file's base
-
# name will begin with the given string. In the latter form,
-
# the temporary file's base name will begin with the array's first
-
# element, and end with the second element. For example:
-
#
-
# file = Tempfile.new('hello')
-
# file.path # => something like: "/tmp/hello2843-8392-92849382--0"
-
#
-
# # Use the Array form to enforce an extension in the filename:
-
# file = Tempfile.new(['hello', '.jpg'])
-
# file.path # => something like: "/tmp/hello2843-8392-92849382--0.jpg"
-
#
-
# The temporary file will be placed in the directory as specified
-
# by the +tmpdir+ parameter. By default, this is +Dir.tmpdir+.
-
# When $SAFE > 0 and the given +tmpdir+ is tainted, it uses
-
# '/tmp' as the temporary directory. Please note that ENV values
-
# are tainted by default, and +Dir.tmpdir+'s return value might
-
# come from environment variables (e.g. <tt>$TMPDIR</tt>).
-
#
-
# file = Tempfile.new('hello', '/home/aisaka')
-
# file.path # => something like: "/home/aisaka/hello2843-8392-92849382--0"
-
#
-
# You can also pass an options hash. Under the hood, Tempfile creates
-
# the temporary file using +File.open+. These options will be passed to
-
# +File.open+. This is mostly useful for specifying encoding
-
# options, e.g.:
-
#
-
# Tempfile.new('hello', '/home/aisaka', :encoding => 'ascii-8bit')
-
#
-
# # You can also omit the 'tmpdir' parameter:
-
# Tempfile.new('hello', :encoding => 'ascii-8bit')
-
#
-
# === Exceptions
-
#
-
# If Tempfile.new cannot find a unique filename within a limited
-
# number of tries, then it will raise an exception.
-
1
def initialize(basename, *rest)
-
@data = []
-
@clean_proc = Remover.new(@data)
-
ObjectSpace.define_finalizer(self, @clean_proc)
-
-
create(basename, *rest) do |tmpname, n, opts|
-
mode = File::RDWR|File::CREAT|File::EXCL
-
perm = 0600
-
if opts
-
mode |= opts.delete(:mode) || 0
-
opts[:perm] = perm
-
perm = nil
-
else
-
opts = perm
-
end
-
self.class.locking(tmpname) do
-
@data[1] = @tmpfile = File.open(tmpname, mode, opts)
-
@data[0] = @tmpname = tmpname
-
end
-
@mode = mode & ~(File::CREAT|File::EXCL)
-
perm or opts.freeze
-
@opts = opts
-
end
-
-
super(@tmpfile)
-
end
-
-
# Opens or reopens the file with mode "r+".
-
1
def open
-
@tmpfile.close if @tmpfile
-
@tmpfile = File.open(@tmpname, @mode, @opts)
-
@data[1] = @tmpfile
-
__setobj__(@tmpfile)
-
end
-
-
1
def _close # :nodoc:
-
@tmpfile.close if @tmpfile
-
@tmpfile = nil
-
@data[1] = nil if @data
-
end
-
1
protected :_close
-
-
# Closes the file. If +unlink_now+ is true, then the file will be unlinked
-
# (deleted) after closing. Of course, you can choose to later call #unlink
-
# if you do not unlink it now.
-
#
-
# If you don't explicitly unlink the temporary file, the removal
-
# will be delayed until the object is finalized.
-
1
def close(unlink_now=false)
-
if unlink_now
-
close!
-
else
-
_close
-
end
-
end
-
-
# Closes and unlinks (deletes) the file. Has the same effect as called
-
# <tt>close(true)</tt>.
-
1
def close!
-
_close
-
unlink
-
ObjectSpace.undefine_finalizer(self)
-
end
-
-
# Unlinks (deletes) the file from the filesystem. One should always unlink
-
# the file after using it, as is explained in the "Explicit close" good
-
# practice section in the Tempfile overview:
-
#
-
# file = Tempfile.new('foo')
-
# begin
-
# ...do something with file...
-
# ensure
-
# file.close
-
# file.unlink # deletes the temp file
-
# end
-
#
-
# === Unlink-before-close
-
#
-
# On POSIX systems it's possible to unlink a file before closing it. This
-
# practice is explained in detail in the Tempfile overview (section
-
# "Unlink after creation"); please refer there for more information.
-
#
-
# However, unlink-before-close may not be supported on non-POSIX operating
-
# systems. Microsoft Windows is the most notable case: unlinking a non-closed
-
# file will result in an error, which this method will silently ignore. If
-
# you want to practice unlink-before-close whenever possible, then you should
-
# write code like this:
-
#
-
# file = Tempfile.new('foo')
-
# file.unlink # On Windows this silently fails.
-
# begin
-
# ... do something with file ...
-
# ensure
-
# file.close! # Closes the file handle. If the file wasn't unlinked
-
# # because #unlink failed, then this method will attempt
-
# # to do so again.
-
# end
-
1
def unlink
-
# keep this order for thread safeness
-
return unless @tmpname
-
begin
-
if File.exist?(@tmpname)
-
File.unlink(@tmpname)
-
end
-
# remove tmpname from remover
-
@data[0] = @data[2] = nil
-
@tmpname = nil
-
rescue Errno::EACCES
-
# may not be able to unlink on Windows; just ignore
-
end
-
end
-
1
alias delete unlink
-
-
# Returns the full path name of the temporary file.
-
# This will be nil if #unlink has been called.
-
1
def path
-
@tmpname
-
end
-
-
# Returns the size of the temporary file. As a side effect, the IO
-
# buffer is flushed before determining the size.
-
1
def size
-
if @tmpfile
-
@tmpfile.flush
-
@tmpfile.stat.size
-
elsif @tmpname
-
File.size(@tmpname)
-
else
-
0
-
end
-
end
-
1
alias length size
-
-
# :stopdoc:
-
1
class Remover
-
1
def initialize(data)
-
@pid = $$
-
@data = data
-
end
-
-
1
def call(*args)
-
if @pid == $$
-
path, tmpfile = *@data
-
-
STDERR.print "removing ", path, "..." if $DEBUG
-
-
tmpfile.close if tmpfile
-
-
# keep this order for thread safeness
-
if path
-
File.unlink(path) if File.exist?(path)
-
end
-
-
STDERR.print "done\n" if $DEBUG
-
end
-
end
-
end
-
# :startdoc:
-
-
1
class << self
-
# Creates a new Tempfile.
-
#
-
# If no block is given, this is a synonym for Tempfile.new.
-
#
-
# If a block is given, then a Tempfile object will be constructed,
-
# and the block is run with said object as argument. The Tempfile
-
# oject will be automatically closed after the block terminates.
-
# The call returns the value of the block.
-
#
-
# In any case, all arguments (+*args+) will be passed to Tempfile.new.
-
#
-
# Tempfile.open('foo', '/home/temp') do |f|
-
# ... do something with f ...
-
# end
-
#
-
# # Equivalent:
-
# f = Tempfile.open('foo', '/home/temp')
-
# begin
-
# ... do something with f ...
-
# ensure
-
# f.close
-
# end
-
1
def open(*args)
-
tempfile = new(*args)
-
-
if block_given?
-
begin
-
yield(tempfile)
-
ensure
-
tempfile.close
-
end
-
else
-
tempfile
-
end
-
end
-
-
# :stopdoc:
-
-
# yields with locking for +tmpname+ and returns the result of the
-
# block.
-
1
def locking(tmpname)
-
lock = tmpname + '.lock'
-
mkdir(lock)
-
yield
-
ensure
-
rmdir(lock) if lock
-
end
-
-
1
def mkdir(*args)
-
Dir.mkdir(*args)
-
end
-
-
1
def rmdir(*args)
-
Dir.rmdir(*args)
-
end
-
end
-
end
-
-
1
if __FILE__ == $0
-
# $DEBUG = true
-
f = Tempfile.new("foo")
-
f.print("foo\n")
-
f.close
-
f.open
-
p f.gets # => "foo\n"
-
f.close!
-
end
-
# Timeout long-running blocks
-
#
-
# == Synopsis
-
#
-
# require 'timeout'
-
# status = Timeout::timeout(5) {
-
# # Something that should be interrupted if it takes more than 5 seconds...
-
# }
-
#
-
# == Description
-
#
-
# Timeout provides a way to auto-terminate a potentially long-running
-
# operation if it hasn't finished in a fixed amount of time.
-
#
-
# Previous versions didn't use a module for namespacing, however
-
# #timeout is provided for backwards compatibility. You
-
# should prefer Timeout#timeout instead.
-
#
-
# == Copyright
-
#
-
# Copyright:: (C) 2000 Network Applied Communication Laboratory, Inc.
-
# Copyright:: (C) 2000 Information-technology Promotion Agency, Japan
-
-
1
module Timeout
-
# Raised by Timeout#timeout when the block times out.
-
1
class Error < RuntimeError
-
end
-
1
class ExitException < ::Exception # :nodoc:
-
end
-
-
# :stopdoc:
-
1
THIS_FILE = /\A#{Regexp.quote(__FILE__)}:/o
-
1
CALLER_OFFSET = ((c = caller[0]) && THIS_FILE =~ c) ? 1 : 0
-
# :startdoc:
-
-
# Perform an operation in a block, timing it out if it takes longer
-
# than +sec+ seconds to complete.
-
#
-
# +sec+:: Number of seconds to wait for the block to terminate. Any number
-
# may be used, including Floats to specify fractional seconds.
-
# +klass+:: Exception Class to raise if the block fails to terminate
-
# in +sec+ seconds. Omitting will use the default, Timeout::Error
-
#
-
# The block will be executed on another thread and will be given one
-
# argument: +sec+.
-
#
-
# Returns the result of the block *if* the block completed before
-
# +sec+ seconds, otherwise throws an exception, based on the value of +klass+.
-
#
-
# Note that this is both a method of module Timeout, so you can <tt>include
-
# Timeout</tt> into your classes so they have a #timeout method, as well as
-
# a module method, so you can call it directly as Timeout.timeout().
-
1
def timeout(sec, klass = nil) #:yield: +sec+
-
return yield(sec) if sec == nil or sec.zero?
-
exception = klass || Class.new(ExitException)
-
begin
-
begin
-
x = Thread.current
-
y = Thread.start {
-
begin
-
sleep sec
-
rescue => e
-
x.raise e
-
else
-
x.raise exception, "execution expired"
-
end
-
}
-
return yield(sec)
-
ensure
-
if y
-
y.kill
-
y.join # make sure y is dead.
-
end
-
end
-
rescue exception => e
-
rej = /\A#{Regexp.quote(__FILE__)}:#{__LINE__-4}\z/o
-
(bt = e.backtrace).reject! {|m| rej =~ m}
-
level = -caller(CALLER_OFFSET).size
-
while THIS_FILE =~ bt[level]
-
bt.delete_at(level)
-
level += 1
-
end
-
raise if klass # if exception class is specified, it
-
# would be expected outside.
-
raise Error, e.message, e.backtrace
-
end
-
end
-
-
1
module_function :timeout
-
end
-
-
# Identical to:
-
#
-
# Timeout::timeout(n, e, &block).
-
#
-
# This method is deprecated and provided only for backwards compatibility.
-
# You should use Timeout#timeout instead.
-
1
def timeout(n, e = nil, &block)
-
Timeout::timeout(n, e, &block)
-
end
-
-
# Another name for Timeout::Error, defined for backwards compatibility with
-
# earlier versions of timeout.rb.
-
1
TimeoutError = Timeout::Error
-
#
-
# tmpdir - retrieve temporary directory path
-
#
-
# $Id: tmpdir.rb 31635 2011-05-18 21:19:18Z drbrain $
-
#
-
-
1
require 'fileutils'
-
1
begin
-
1
require 'etc.so'
-
rescue LoadError
-
end
-
-
1
class Dir
-
-
1
@@systmpdir ||= defined?(Etc.systmpdir) ? Etc.systmpdir : '/tmp'
-
-
##
-
# Returns the operating system's temporary file path.
-
-
1
def Dir::tmpdir
-
2
tmp = '.'
-
2
if $SAFE > 0
-
tmp = @@systmpdir
-
else
-
2
for dir in [ENV['TMPDIR'], ENV['TMP'], ENV['TEMP'], @@systmpdir, '/tmp']
-
if dir and stat = File.stat(dir) and stat.directory? and stat.writable?
-
2
tmp = dir
-
2
break
-
8
end rescue nil
-
end
-
2
File.expand_path(tmp)
-
end
-
end
-
-
# Dir.mktmpdir creates a temporary directory.
-
#
-
# The directory is created with 0700 permission.
-
#
-
# The prefix and suffix of the name of the directory is specified by
-
# the optional first argument, <i>prefix_suffix</i>.
-
# - If it is not specified or nil, "d" is used as the prefix and no suffix is used.
-
# - If it is a string, it is used as the prefix and no suffix is used.
-
# - If it is an array, first element is used as the prefix and second element is used as a suffix.
-
#
-
# Dir.mktmpdir {|dir| dir is ".../d..." }
-
# Dir.mktmpdir("foo") {|dir| dir is ".../foo..." }
-
# Dir.mktmpdir(["foo", "bar"]) {|dir| dir is ".../foo...bar" }
-
#
-
# The directory is created under Dir.tmpdir or
-
# the optional second argument <i>tmpdir</i> if non-nil value is given.
-
#
-
# Dir.mktmpdir {|dir| dir is "#{Dir.tmpdir}/d..." }
-
# Dir.mktmpdir(nil, "/var/tmp") {|dir| dir is "/var/tmp/d..." }
-
#
-
# If a block is given,
-
# it is yielded with the path of the directory.
-
# The directory and its contents are removed
-
# using FileUtils.remove_entry_secure before Dir.mktmpdir returns.
-
# The value of the block is returned.
-
#
-
# Dir.mktmpdir {|dir|
-
# # use the directory...
-
# open("#{dir}/foo", "w") { ... }
-
# }
-
#
-
# If a block is not given,
-
# The path of the directory is returned.
-
# In this case, Dir.mktmpdir doesn't remove the directory.
-
#
-
# dir = Dir.mktmpdir
-
# begin
-
# # use the directory...
-
# open("#{dir}/foo", "w") { ... }
-
# ensure
-
# # remove the directory.
-
# FileUtils.remove_entry_secure dir
-
# end
-
#
-
1
def Dir.mktmpdir(prefix_suffix=nil, *rest)
-
path = Tmpname.create(prefix_suffix || "d", *rest) {|n| mkdir(n, 0700)}
-
if block_given?
-
begin
-
yield path
-
ensure
-
FileUtils.remove_entry_secure path
-
end
-
else
-
path
-
end
-
end
-
-
1
module Tmpname # :nodoc:
-
1
module_function
-
-
1
def tmpdir
-
Dir.tmpdir
-
end
-
-
1
def make_tmpname(prefix_suffix, n)
-
case prefix_suffix
-
when String
-
prefix = prefix_suffix
-
suffix = ""
-
when Array
-
prefix = prefix_suffix[0]
-
suffix = prefix_suffix[1]
-
else
-
raise ArgumentError, "unexpected prefix_suffix: #{prefix_suffix.inspect}"
-
end
-
t = Time.now.strftime("%Y%m%d")
-
path = "#{prefix}#{t}-#{$$}-#{rand(0x100000000).to_s(36)}"
-
path << "-#{n}" if n
-
path << suffix
-
end
-
-
1
def create(basename, *rest)
-
if opts = Hash.try_convert(rest[-1])
-
opts = opts.dup if rest.pop.equal?(opts)
-
max_try = opts.delete(:max_try)
-
opts = [opts]
-
else
-
opts = []
-
end
-
tmpdir, = *rest
-
if $SAFE > 0 and tmpdir.tainted?
-
tmpdir = '/tmp'
-
else
-
tmpdir ||= tmpdir()
-
end
-
n = nil
-
begin
-
path = File.expand_path(make_tmpname(basename, n), tmpdir)
-
yield(path, n, *opts)
-
rescue Errno::EEXIST
-
n ||= 0
-
n += 1
-
retry if !max_try or n < max_try
-
raise "cannot generate temporary name using `#{basename}' under `#{tmpdir}'"
-
end
-
path
-
end
-
end
-
end
-
#--
-
# Copyright (c) 2004-2012 David Heinemeier Hansson
-
#
-
# Permission is hereby granted, free of charge, to any person obtaining
-
# a copy of this software and associated documentation files (the
-
# "Software"), to deal in the Software without restriction, including
-
# without limitation the rights to use, copy, modify, merge, publish,
-
# distribute, sublicense, and/or sell copies of the Software, and to
-
# permit persons to whom the Software is furnished to do so, subject to
-
# the following conditions:
-
#
-
# The above copyright notice and this permission notice shall be
-
# included in all copies or substantial portions of the Software.
-
#
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
#++
-
-
1
require 'simplecov'
-
1
SimpleCov.start
-
1
require 'abstract_controller'
-
1
require 'action_view'
-
1
require 'action_mailer/version'
-
-
# Common Active Support usage in Action Mailer
-
1
require 'active_support/rails'
-
1
require 'active_support/core_ext/class'
-
1
require 'active_support/core_ext/module/attr_internal'
-
1
require 'active_support/core_ext/string/inflections'
-
1
require 'active_support/lazy_load_hooks'
-
-
1
module ActionMailer
-
1
extend ::ActiveSupport::Autoload
-
-
1
eager_autoload do
-
1
autoload :Collector
-
end
-
-
1
autoload :Base
-
1
autoload :DeliveryMethods
-
1
autoload :MailHelper
-
1
autoload :TestCase
-
1
autoload :TestHelper
-
end
-
1
require 'mail'
-
1
require 'action_mailer/queued_message'
-
1
require 'action_mailer/collector'
-
1
require 'active_support/core_ext/string/inflections'
-
1
require 'active_support/core_ext/hash/except'
-
1
require 'active_support/core_ext/module/anonymous'
-
1
require 'active_support/queueing'
-
1
require 'action_mailer/log_subscriber'
-
-
1
module ActionMailer
-
# Action Mailer allows you to send email from your application using a mailer model and views.
-
#
-
# = Mailer Models
-
#
-
# To use Action Mailer, you need to create a mailer model.
-
#
-
# $ rails generate mailer Notifier
-
#
-
# The generated model inherits from <tt>ActionMailer::Base</tt>. A mailer model defines methods
-
# used to generate an email message. In these methods, you can setup variables to be used in
-
# the mailer views, options on the mail itself such as the <tt>:from</tt> address, and attachments.
-
#
-
# Examples:
-
#
-
# class Notifier < ActionMailer::Base
-
# default from: 'no-reply@example.com',
-
# return_path: 'system@example.com'
-
#
-
# def welcome(recipient)
-
# @account = recipient
-
# mail(to: recipient.email_address_with_name,
-
# bcc: ["bcc@example.com", "Order Watcher <watcher@example.com>"])
-
# end
-
# end
-
#
-
# Within the mailer method, you have access to the following methods:
-
#
-
# * <tt>attachments[]=</tt> - Allows you to add attachments to your email in an intuitive
-
# manner; <tt>attachments['filename.png'] = File.read('path/to/filename.png')</tt>
-
#
-
# * <tt>attachments.inline[]=</tt> - Allows you to add an inline attachment to your email
-
# in the same manner as <tt>attachments[]=</tt>
-
#
-
# * <tt>headers[]=</tt> - Allows you to specify any header field in your email such
-
# as <tt>headers['X-No-Spam'] = 'True'</tt>. Note, while most fields like <tt>To:</tt>
-
# <tt>From:</tt> can only appear once in an email header, other fields like <tt>X-Anything</tt>
-
# can appear multiple times. If you want to change a field that can appear multiple times,
-
# you need to set it to nil first so that Mail knows you are replacing it and not adding
-
# another field of the same name.
-
#
-
# * <tt>headers(hash)</tt> - Allows you to specify multiple headers in your email such
-
# as <tt>headers({'X-No-Spam' => 'True', 'In-Reply-To' => '1234@message.id'})</tt>
-
#
-
# * <tt>mail</tt> - Allows you to specify email to be sent.
-
#
-
# The hash passed to the mail method allows you to specify any header that a Mail::Message
-
# will accept (any valid Email header including optional fields).
-
#
-
# The mail method, if not passed a block, will inspect your views and send all the views with
-
# the same name as the method, so the above action would send the +welcome.text.erb+ view
-
# file as well as the +welcome.text.html.erb+ view file in a +multipart/alternative+ email.
-
#
-
# If you want to explicitly render only certain templates, pass a block:
-
#
-
# mail(to: user.email) do |format|
-
# format.text
-
# format.html
-
# end
-
#
-
# The block syntax is also useful in providing information specific to a part:
-
#
-
# mail(to: user.email) do |format|
-
# format.text(:content_transfer_encoding => "base64")
-
# format.html
-
# end
-
#
-
# Or even to render a special view:
-
#
-
# mail(to: user.email) do |format|
-
# format.text
-
# format.html { render "some_other_template" }
-
# end
-
#
-
# = Mailer views
-
#
-
# Like Action Controller, each mailer class has a corresponding view directory in which each
-
# method of the class looks for a template with its name.
-
#
-
# To define a template to be used with a mailing, create an <tt>.erb</tt> file with the same
-
# name as the method in your mailer model. For example, in the mailer defined above, the template at
-
# <tt>app/views/notifier/welcome.text.erb</tt> would be used to generate the email.
-
#
-
# Variables defined in the model are accessible as instance variables in the view.
-
#
-
# Emails by default are sent in plain text, so a sample view for our model example might look like this:
-
#
-
# Hi <%= @account.name %>,
-
# Thanks for joining our service! Please check back often.
-
#
-
# You can even use Action Pack helpers in these views. For example:
-
#
-
# You got a new note!
-
# <%= truncate(@note.body, length: 25) %>
-
#
-
# If you need to access the subject, from or the recipients in the view, you can do that through message object:
-
#
-
# You got a new note from <%= message.from %>!
-
# <%= truncate(@note.body, length: 25) %>
-
#
-
#
-
# = Generating URLs
-
#
-
# URLs can be generated in mailer views using <tt>url_for</tt> or named routes. Unlike controllers from
-
# Action Pack, the mailer instance doesn't have any context about the incoming request, so you'll need
-
# to provide all of the details needed to generate a URL.
-
#
-
# When using <tt>url_for</tt> you'll need to provide the <tt>:host</tt>, <tt>:controller</tt>, and <tt>:action</tt>:
-
#
-
# <%= url_for(host: "example.com", controller: "welcome", action: "greeting") %>
-
#
-
# When using named routes you only need to supply the <tt>:host</tt>:
-
#
-
# <%= users_url(host: "example.com") %>
-
#
-
# You should use the <tt>named_route_url</tt> style (which generates absolute URLs) and avoid using the
-
# <tt>named_route_path</tt> style (which generates relative URLs), since clients reading the mail will
-
# have no concept of a current URL from which to determine a relative path.
-
#
-
# It is also possible to set a default host that will be used in all mailers by setting the <tt>:host</tt>
-
# option as a configuration option in <tt>config/application.rb</tt>:
-
#
-
# config.action_mailer.default_url_options = { :host => "example.com" }
-
#
-
# When you decide to set a default <tt>:host</tt> for your mailers, then you need to make sure to use the
-
# <tt>:only_path => false</tt> option when using <tt>url_for</tt>. Since the <tt>url_for</tt> view helper
-
# will generate relative URLs by default when a <tt>:host</tt> option isn't explicitly provided, passing
-
# <tt>:only_path => false</tt> will ensure that absolute URLs are generated.
-
#
-
# = Sending mail
-
#
-
# Once a mailer action and template are defined, you can deliver your message or create it and save it
-
# for delivery later:
-
#
-
# Notifier.welcome(david).deliver # sends the email
-
# mail = Notifier.welcome(david) # => a Mail::Message object
-
# mail.deliver # sends the email
-
#
-
# You never instantiate your mailer class. Rather, you just call the method you defined on the class itself.
-
#
-
# = Multipart Emails
-
#
-
# Multipart messages can also be used implicitly because Action Mailer will automatically detect and use
-
# multipart templates, where each template is named after the name of the action, followed by the content
-
# type. Each such detected template will be added as a separate part to the message.
-
#
-
# For example, if the following templates exist:
-
# * signup_notification.text.erb
-
# * signup_notification.text.html.erb
-
# * signup_notification.text.xml.builder
-
# * signup_notification.text.yaml.erb
-
#
-
# Each would be rendered and added as a separate part to the message, with the corresponding content
-
# type. The content type for the entire message is automatically set to <tt>multipart/alternative</tt>,
-
# which indicates that the email contains multiple different representations of the same email
-
# body. The same instance variables defined in the action are passed to all email templates.
-
#
-
# Implicit template rendering is not performed if any attachments or parts have been added to the email.
-
# This means that you'll have to manually add each part to the email and set the content type of the email
-
# to <tt>multipart/alternative</tt>.
-
#
-
# = Attachments
-
#
-
# Sending attachment in emails is easy:
-
#
-
# class ApplicationMailer < ActionMailer::Base
-
# def welcome(recipient)
-
# attachments['free_book.pdf'] = File.read('path/to/file.pdf')
-
# mail(to: recipient, subject: "New account information")
-
# end
-
# end
-
#
-
# Which will (if it had both a <tt>welcome.text.erb</tt> and <tt>welcome.text.html.erb</tt>
-
# template in the view directory), send a complete <tt>multipart/mixed</tt> email with two parts,
-
# the first part being a <tt>multipart/alternative</tt> with the text and HTML email parts inside,
-
# and the second being a <tt>application/pdf</tt> with a Base64 encoded copy of the file.pdf book
-
# with the filename +free_book.pdf+.
-
#
-
# If you need to send attachments with no content, you need to create an empty view for it,
-
# or add an empty body parameter like this:
-
#
-
# class ApplicationMailer < ActionMailer::Base
-
# def welcome(recipient)
-
# attachments['free_book.pdf'] = File.read('path/to/file.pdf')
-
# mail(to: recipient, subject: "New account information", body: "")
-
# end
-
# end
-
#
-
# = Inline Attachments
-
#
-
# You can also specify that a file should be displayed inline with other HTML. This is useful
-
# if you want to display a corporate logo or a photo.
-
#
-
# class ApplicationMailer < ActionMailer::Base
-
# def welcome(recipient)
-
# attachments.inline['photo.png'] = File.read('path/to/photo.png')
-
# mail(to: recipient, subject: "Here is what we look like")
-
# end
-
# end
-
#
-
# And then to reference the image in the view, you create a <tt>welcome.html.erb</tt> file and
-
# make a call to +image_tag+ passing in the attachment you want to display and then call
-
# +url+ on the attachment to get the relative content id path for the image source:
-
#
-
# <h1>Please Don't Cringe</h1>
-
#
-
# <%= image_tag attachments['photo.png'].url -%>
-
#
-
# As we are using Action View's +image_tag+ method, you can pass in any other options you want:
-
#
-
# <h1>Please Don't Cringe</h1>
-
#
-
# <%= image_tag attachments['photo.png'].url, alt: 'Our Photo', class: 'photo' -%>
-
#
-
# = Observing and Intercepting Mails
-
#
-
# Action Mailer provides hooks into the Mail observer and interceptor methods. These allow you to
-
# register classes that are called during the mail delivery life cycle.
-
#
-
# An observer class must implement the <tt>:delivered_email(message)</tt> method which will be
-
# called once for every email sent after the email has been sent.
-
#
-
# An interceptor class must implement the <tt>:delivering_email(message)</tt> method which will be
-
# called before the email is sent, allowing you to make modifications to the email before it hits
-
# the delivery agents. Your class should make any needed modifications directly to the passed
-
# in Mail::Message instance.
-
#
-
# = Default Hash
-
#
-
# Action Mailer provides some intelligent defaults for your emails, these are usually specified in a
-
# default method inside the class definition:
-
#
-
# class Notifier < ActionMailer::Base
-
# default sender: 'system@example.com'
-
# end
-
#
-
# You can pass in any header value that a <tt>Mail::Message</tt> accepts. Out of the box,
-
# <tt>ActionMailer::Base</tt> sets the following:
-
#
-
# * <tt>:mime_version => "1.0"</tt>
-
# * <tt>:charset => "UTF-8",</tt>
-
# * <tt>:content_type => "text/plain",</tt>
-
# * <tt>:parts_order => [ "text/plain", "text/enriched", "text/html" ]</tt>
-
#
-
# <tt>parts_order</tt> and <tt>charset</tt> are not actually valid <tt>Mail::Message</tt> header fields,
-
# but Action Mailer translates them appropriately and sets the correct values.
-
#
-
# As you can pass in any header, you need to either quote the header as a string, or pass it in as
-
# an underscored symbol, so the following will work:
-
#
-
# class Notifier < ActionMailer::Base
-
# default 'Content-Transfer-Encoding' => '7bit',
-
# content_description: 'This is a description'
-
# end
-
#
-
# Finally, Action Mailer also supports passing <tt>Proc</tt> objects into the default hash, so you
-
# can define methods that evaluate as the message is being generated:
-
#
-
# class Notifier < ActionMailer::Base
-
# default 'X-Special-Header' => Proc.new { my_method }
-
#
-
# private
-
#
-
# def my_method
-
# 'some complex call'
-
# end
-
# end
-
#
-
# Note that the proc is evaluated right at the start of the mail message generation, so if you
-
# set something in the defaults using a proc, and then set the same thing inside of your
-
# mailer method, it will get over written by the mailer method.
-
#
-
# It is also possible to set these default options that will be used in all mailers through
-
# the <tt>default_options=</tt> configuration in <tt>config/application.rb</tt>:
-
#
-
# config.action_mailer.default_options = { from: "no-reply@example.org" }
-
#
-
# = Callbacks
-
#
-
# You can specify callbacks using before_filter and after_filter for configuring your messages.
-
# This may be useful, for example, when you want to add default inline attachments for all
-
# messages sent out by a certain mailer class:
-
#
-
# class Notifier < ActionMailer::Base
-
# before_filter :add_inline_attachment!
-
#
-
# def welcome
-
# mail
-
# end
-
#
-
# private
-
#
-
# def add_inline_attachment!
-
# attachments.inline["footer.jpg"] = File.read('/path/to/filename.jpg')
-
# end
-
# end
-
#
-
# Callbacks in ActionMailer are implemented using AbstractController::Callbacks, so you
-
# can define and configure callbacks in the same manner that you would use callbacks in
-
# classes that inherit from ActionController::Base.
-
#
-
# Note that unless you have a specific reason to do so, you should prefer using before_filter
-
# rather than after_filter in your ActionMailer classes so that headers are parsed properly.
-
#
-
# = Configuration options
-
#
-
# These options are specified on the class level, like
-
# <tt>ActionMailer::Base.raise_delivery_errors = true</tt>
-
#
-
# * <tt>default</tt> - You can pass this in at a class level as well as within the class itself as
-
# per the above section.
-
#
-
# * <tt>logger</tt> - the logger is used for generating information on the mailing run if available.
-
# Can be set to nil for no logging. Compatible with both Ruby's own Logger and Log4r loggers.
-
#
-
# * <tt>smtp_settings</tt> - Allows detailed configuration for <tt>:smtp</tt> delivery method:
-
# * <tt>:address</tt> - Allows you to use a remote mail server. Just change it from its default
-
# "localhost" setting.
-
# * <tt>:port</tt> - On the off chance that your mail server doesn't run on port 25, you can change it.
-
# * <tt>:domain</tt> - If you need to specify a HELO domain, you can do it here.
-
# * <tt>:user_name</tt> - If your mail server requires authentication, set the username in this setting.
-
# * <tt>:password</tt> - If your mail server requires authentication, set the password in this setting.
-
# * <tt>:authentication</tt> - If your mail server requires authentication, you need to specify the
-
# authentication type here.
-
# This is a symbol and one of <tt>:plain</tt> (will send the password in the clear), <tt>:login</tt> (will
-
# send password Base64 encoded) or <tt>:cram_md5</tt> (combines a Challenge/Response mechanism to exchange
-
# information and a cryptographic Message Digest 5 algorithm to hash important information)
-
# * <tt>:enable_starttls_auto</tt> - When set to true, detects if STARTTLS is enabled in your SMTP server
-
# and starts to use it.
-
# * <tt>:openssl_verify_mode</tt> - When using TLS, you can set how OpenSSL checks the certificate. This is
-
# really useful if you need to validate a self-signed and/or a wildcard certificate. You can use the name
-
# of an OpenSSL verify constant ('none', 'peer', 'client_once','fail_if_no_peer_cert') or directly the
-
# constant (OpenSSL::SSL::VERIFY_NONE, OpenSSL::SSL::VERIFY_PEER,...).
-
#
-
# * <tt>sendmail_settings</tt> - Allows you to override options for the <tt>:sendmail</tt> delivery method.
-
# * <tt>:location</tt> - The location of the sendmail executable. Defaults to <tt>/usr/sbin/sendmail</tt>.
-
# * <tt>:arguments</tt> - The command line arguments. Defaults to <tt>-i -t</tt> with <tt>-f sender@address</tt>
-
# added automatically before the message is sent.
-
#
-
# * <tt>file_settings</tt> - Allows you to override options for the <tt>:file</tt> delivery method.
-
# * <tt>:location</tt> - The directory into which emails will be written. Defaults to the application
-
# <tt>tmp/mails</tt>.
-
#
-
# * <tt>raise_delivery_errors</tt> - Whether or not errors should be raised if the email fails to be delivered.
-
#
-
# * <tt>delivery_method</tt> - Defines a delivery method. Possible values are <tt>:smtp</tt> (default),
-
# <tt>:sendmail</tt>, <tt>:test</tt>, and <tt>:file</tt>. Or you may provide a custom delivery method
-
# object e.g. MyOwnDeliveryMethodClass. See the Mail gem documentation on the interface you need to
-
# implement for a custom delivery agent.
-
#
-
# * <tt>perform_deliveries</tt> - Determines whether emails are actually sent from Action Mailer when you
-
# call <tt>.deliver</tt> on an mail message or on an Action Mailer method. This is on by default but can
-
# be turned off to aid in functional testing.
-
#
-
# * <tt>deliveries</tt> - Keeps an array of all the emails sent out through the Action Mailer with
-
# <tt>delivery_method :test</tt>. Most useful for unit and functional testing.
-
#
-
# * <tt>queue</> - The queue that will be used to deliver the mail. The queue should expect a job that responds to <tt>run</tt>.
-
1
class Base < AbstractController::Base
-
1
include DeliveryMethods
-
1
abstract!
-
-
1
include AbstractController::Logger
-
1
include AbstractController::Rendering
-
1
include AbstractController::Layouts
-
1
include AbstractController::Helpers
-
1
include AbstractController::Translation
-
1
include AbstractController::AssetPaths
-
1
include AbstractController::Callbacks
-
-
1
self.protected_instance_variables = [:@_action_has_layout]
-
-
1
helper ActionMailer::MailHelper
-
-
1
private_class_method :new #:nodoc:
-
-
1
class_attribute :default_params
-
1
self.default_params = {
-
mime_version: "1.0",
-
charset: "UTF-8",
-
content_type: "text/plain",
-
parts_order: [ "text/plain", "text/enriched", "text/html" ]
-
}.freeze
-
-
1
class_attribute :queue
-
1
self.queue = ActiveSupport::SynchronousQueue.new
-
-
1
class << self
-
# Register one or more Observers which will be notified when mail is delivered.
-
1
def register_observers(*observers)
-
3
observers.flatten.compact.each { |observer| register_observer(observer) }
-
end
-
-
# Register one or more Interceptors which will be called before mail is sent.
-
1
def register_interceptors(*interceptors)
-
3
interceptors.flatten.compact.each { |interceptor| register_interceptor(interceptor) }
-
end
-
-
# Register an Observer which will be notified when mail is delivered.
-
# Either a class or a string can be passed in as the Observer. If a string is passed in
-
# it will be <tt>constantize</tt>d.
-
1
def register_observer(observer)
-
4
delivery_observer = (observer.is_a?(String) ? observer.constantize : observer)
-
4
Mail.register_observer(delivery_observer)
-
end
-
-
# Register an Interceptor which will be called before mail is sent.
-
# Either a class or a string can be passed in as the Interceptor. If a string is passed in
-
# it will be <tt>constantize</tt>d.
-
1
def register_interceptor(interceptor)
-
4
delivery_interceptor = (interceptor.is_a?(String) ? interceptor.constantize : interceptor)
-
4
Mail.register_interceptor(delivery_interceptor)
-
end
-
-
1
def mailer_name
-
317
@mailer_name ||= anonymous? ? "anonymous" : name.underscore
-
end
-
1
attr_writer :mailer_name
-
1
alias :controller_path :mailer_name
-
-
1
def default(value = nil)
-
150
self.default_params = default_params.merge(value).freeze if value
-
150
default_params
-
end
-
# Allows to set defaults through app configuration:
-
#
-
# config.action_mailer.default_options = { from: "no-reply@example.org" }
-
1
alias :default_options= :default
-
-
# Receives a raw email, parses it into an email object, decodes it,
-
# instantiates a new mailer, and passes the email object to the mailer
-
# object's +receive+ method. If you want your mailer to be able to
-
# process incoming messages, you'll need to implement a +receive+
-
# method that accepts the raw email string as a parameter:
-
#
-
# class MyMailer < ActionMailer::Base
-
# def receive(mail)
-
# ...
-
# end
-
# end
-
1
def receive(raw_mail)
-
1
ActiveSupport::Notifications.instrument("receive.action_mailer") do |payload|
-
1
mail = Mail.new(raw_mail)
-
1
set_payload_for_mail(payload, mail)
-
1
new.receive(mail)
-
end
-
end
-
-
# Wraps an email delivery inside of Active Support Notifications instrumentation. This
-
# method is actually called by the <tt>Mail::Message</tt> object itself through a callback
-
# when you call <tt>:deliver</tt> on the Mail::Message, calling +deliver_mail+ directly
-
# and passing a Mail::Message will do nothing except tell the logger you sent the email.
-
1
def deliver_mail(mail) #:nodoc:
-
36
ActiveSupport::Notifications.instrument("deliver.action_mailer") do |payload|
-
36
set_payload_for_mail(payload, mail)
-
36
yield # Let Mail do the delivery actions
-
end
-
end
-
-
1
def respond_to?(method, include_private = false) #:nodoc:
-
94
super || action_methods.include?(method.to_s)
-
end
-
-
1
protected
-
-
1
def set_payload_for_mail(payload, mail) #:nodoc:
-
37
payload[:mailer] = name
-
37
payload[:message_id] = mail.message_id
-
37
payload[:subject] = mail.subject
-
37
payload[:to] = mail.to
-
37
payload[:from] = mail.from
-
37
payload[:bcc] = mail.bcc if mail.bcc.present?
-
37
payload[:cc] = mail.cc if mail.cc.present?
-
37
payload[:date] = mail.date
-
37
payload[:mail] = mail.encoded
-
end
-
-
1
def method_missing(method_name, *args)
-
135
if action_methods.include?(method_name.to_s)
-
122
QueuedMessage.new(queue, self, method_name, *args)
-
else
-
13
super
-
end
-
end
-
end
-
-
1
attr_internal :message
-
-
# Instantiate a new mailer object. If +method_name+ is not +nil+, the mailer
-
# will be initialized according to the named method. If not, the mailer will
-
# remain uninitialized (useful when you only need to invoke the "receive"
-
# method, for instance).
-
1
def initialize(method_name=nil, *args)
-
121
super()
-
121
@_message = Mail.new
-
121
process(method_name, *args) if method_name
-
end
-
-
1
def process(*args) #:nodoc:
-
120
lookup_context.skip_default_locale!
-
-
120
generated_mail = super
-
118
unless generated_mail
-
1
@_message = NullMail.new
-
end
-
end
-
-
1
class NullMail #:nodoc:
-
2
def body; '' end
-
-
1
def method_missing(*args)
-
nil
-
end
-
end
-
-
1
def mailer_name
-
2
self.class.mailer_name
-
end
-
-
# Allows you to pass random and unusual headers to the new <tt>Mail::Message</tt> object
-
# which will add them to itself.
-
#
-
# headers['X-Special-Domain-Specific-Header'] = "SecretValue"
-
#
-
# You can also pass a hash into headers of header field names and values, which
-
# will then be set on the Mail::Message object:
-
#
-
# headers 'X-Special-Domain-Specific-Header' => "SecretValue",
-
# 'In-Reply-To' => incoming.message_id
-
#
-
# The resulting Mail::Message will have the following in its header:
-
#
-
# X-Special-Domain-Specific-Header: SecretValue
-
1
def headers(args=nil)
-
32
if args
-
3
@_message.headers(args)
-
else
-
29
@_message
-
end
-
end
-
-
# Allows you to add attachments to an email, like so:
-
#
-
# mail.attachments['filename.jpg'] = File.read('/path/to/filename.jpg')
-
#
-
# If you do this, then Mail will take the file name and work out the mime type
-
# set the Content-Type, Content-Disposition, Content-Transfer-Encoding and
-
# base64 encode the contents of the attachment all for you.
-
#
-
# You can also specify overrides if you want by passing a hash instead of a string:
-
#
-
# mail.attachments['filename.jpg'] = {mime_type: 'application/x-gzip',
-
# content: File.read('/path/to/filename.jpg')}
-
#
-
# If you want to use a different encoding than Base64, you can pass an encoding in,
-
# but then it is up to you to pass in the content pre-encoded, and don't expect
-
# Mail to know how to decode this data:
-
#
-
# file_content = SpecialEncode(File.read('/path/to/filename.jpg'))
-
# mail.attachments['filename.jpg'] = {mime_type: 'application/x-gzip',
-
# encoding: 'SpecialEncoding',
-
# content: file_content }
-
#
-
# You can also search for specific attachments:
-
#
-
# # By Filename
-
# mail.attachments['filename.jpg'] # => Mail::Part object or nil
-
#
-
# # or by index
-
# mail.attachments[0] # => Mail::Part (first attachment)
-
#
-
1
def attachments
-
12
@_message.attachments
-
end
-
-
# The main method that creates the message and renders the email templates. There are
-
# two ways to call this method, with a block, or without a block.
-
#
-
# Both methods accept a headers hash. This hash allows you to specify the most used headers
-
# in an email message, these are:
-
#
-
# * <tt>:subject</tt> - The subject of the message, if this is omitted, Action Mailer will
-
# ask the Rails I18n class for a translated <tt>:subject</tt> in the scope of
-
# <tt>[mailer_scope, action_name]</tt> or if this is missing, will translate the
-
# humanized version of the <tt>action_name</tt>
-
# * <tt>:to</tt> - Who the message is destined for, can be a string of addresses, or an array
-
# of addresses.
-
# * <tt>:from</tt> - Who the message is from
-
# * <tt>:cc</tt> - Who you would like to Carbon-Copy on this email, can be a string of addresses,
-
# or an array of addresses.
-
# * <tt>:bcc</tt> - Who you would like to Blind-Carbon-Copy on this email, can be a string of
-
# addresses, or an array of addresses.
-
# * <tt>:reply_to</tt> - Who to set the Reply-To header of the email to.
-
# * <tt>:date</tt> - The date to say the email was sent on.
-
#
-
# You can set default values for any of the above headers (except :date) by using the <tt>default</tt>
-
# class method:
-
#
-
# class Notifier < ActionMailer::Base
-
# self.default from: 'no-reply@test.lindsaar.net',
-
# bcc: 'email_logger@test.lindsaar.net',
-
# reply_to: 'bounces@test.lindsaar.net'
-
# end
-
#
-
# If you need other headers not listed above, you can either pass them in
-
# as part of the headers hash or use the <tt>headers['name'] = value</tt>
-
# method.
-
#
-
# When a <tt>:return_path</tt> is specified as header, that value will be used as the 'envelope from'
-
# address for the Mail message. Setting this is useful when you want delivery notifications
-
# sent to a different address than the one in <tt>:from</tt>. Mail will actually use the
-
# <tt>:return_path</tt> in preference to the <tt>:sender</tt> in preference to the <tt>:from</tt>
-
# field for the 'envelope from' value.
-
#
-
# If you do not pass a block to the +mail+ method, it will find all templates in the
-
# view paths using by default the mailer name and the method name that it is being
-
# called from, it will then create parts for each of these templates intelligently,
-
# making educated guesses on correct content type and sequence, and return a fully
-
# prepared Mail::Message ready to call <tt>:deliver</tt> on to send.
-
#
-
# For example:
-
#
-
# class Notifier < ActionMailer::Base
-
# default from: 'no-reply@test.lindsaar.net',
-
#
-
# def welcome
-
# mail(to: 'mikel@test.lindsaar.net')
-
# end
-
# end
-
#
-
# Will look for all templates at "app/views/notifier" with name "welcome".
-
# If no welcome template exists, it will raise an ActionView::MissingTemplate error.
-
#
-
# However, those can be customized:
-
#
-
# mail(template_path: 'notifications', template_name: 'another')
-
#
-
# And now it will look for all templates at "app/views/notifications" with name "another".
-
#
-
# If you do pass a block, you can render specific templates of your choice:
-
#
-
# mail(to: 'mikel@test.lindsaar.net') do |format|
-
# format.text
-
# format.html
-
# end
-
#
-
# You can even render text directly without using a template:
-
#
-
# mail(to: 'mikel@test.lindsaar.net') do |format|
-
# format.text { render text: "Hello Mikel!" }
-
# format.html { render text: "<h1>Hello Mikel!</h1>" }
-
# end
-
#
-
# Which will render a <tt>multipart/alternative</tt> email with <tt>text/plain</tt> and
-
# <tt>text/html</tt> parts.
-
#
-
# The block syntax also allows you to customize the part headers if desired:
-
#
-
# mail(:to => 'mikel@test.lindsaar.net') do |format|
-
# format.text(content_transfer_encoding: "base64")
-
# format.html
-
# end
-
#
-
1
def mail(headers={}, &block)
-
119
m = @_message
-
-
# At the beginning, do not consider class default for parts order neither content_type
-
119
content_type = headers[:content_type]
-
119
parts_order = headers[:parts_order]
-
-
# Call all the procs (if any)
-
119
class_default = self.class.default
-
119
default_values = class_default.merge(class_default) do |k,v|
-
739
v.respond_to?(:to_proc) ? instance_eval(&v) : v
-
end
-
-
# Handle defaults
-
119
headers = headers.reverse_merge(default_values)
-
119
headers[:subject] ||= default_i18n_subject
-
-
# Apply charset at the beginning so all fields are properly quoted
-
119
m.charset = charset = headers[:charset]
-
-
# Set configure delivery behavior
-
119
wrap_delivery_behavior!(headers.delete(:delivery_method),headers.delete(:delivery_method_options))
-
-
# Assign all headers except parts_order, content_type and body
-
118
assignable = headers.except(:parts_order, :content_type, :body, :template_name, :template_path)
-
767
assignable.each { |k, v| m[k] = v }
-
-
# Render the templates and blocks
-
118
responses, explicit_order = collect_responses_and_parts_order(headers, &block)
-
117
create_parts_from_responses(m, responses)
-
-
# Setup content type, reapply charset and handle parts order
-
117
m.content_type = set_content_type(m, content_type, headers[:content_type])
-
117
m.charset = charset
-
-
117
if m.multipart?
-
31
parts_order ||= explicit_order || headers[:parts_order]
-
31
m.body.set_sort_order(parts_order)
-
31
m.body.sort_parts!
-
end
-
-
117
m
-
end
-
-
1
protected
-
-
1
def set_content_type(m, user_content_type, class_default)
-
117
params = m.content_type_parameters || {}
-
case
-
when user_content_type.present?
-
3
user_content_type
-
when m.has_attachments?
-
24
if m.attachments.detect { |a| a.inline? }
-
2
["multipart", "related", params]
-
else
-
10
["multipart", "mixed", params]
-
end
-
when m.multipart?
-
17
["multipart", "alternative", params]
-
else
-
85
m.content_type || class_default
-
117
end
-
end
-
-
# Translates the +subject+ using Rails I18n class under <tt>[mailer_scope, action_name]</tt> scope.
-
# If it does not find a translation for the +subject+ under the specified scope it will default to a
-
# humanized version of the <tt>action_name</tt>.
-
1
def default_i18n_subject #:nodoc:
-
69
mailer_scope = self.class.mailer_name.tr('/', '.')
-
69
I18n.t(:subject, scope: [mailer_scope, action_name], default: action_name.humanize)
-
end
-
-
1
def collect_responses_and_parts_order(headers) #:nodoc:
-
118
responses, parts_order = [], nil
-
-
118
if block_given?
-
24
collector = ActionMailer::Collector.new(lookup_context) { render(action_name) }
-
20
yield(collector)
-
53
parts_order = collector.responses.map { |r| r[:content_type] }
-
20
responses = collector.responses
-
elsif headers[:body]
-
responses << {
-
body: headers.delete(:body),
-
content_type: self.class.default[:content_type] || "text/plain"
-
17
}
-
else
-
81
templates_path = headers.delete(:template_path) || self.class.mailer_name
-
81
templates_name = headers.delete(:template_name) || action_name
-
-
81
each_template(templates_path, templates_name) do |template|
-
89
self.formats = template.formats
-
-
responses << {
-
body: render(template: template),
-
content_type: template.type.to_s
-
89
}
-
end
-
end
-
-
117
[responses, parts_order]
-
end
-
-
1
def each_template(paths, name, &block) #:nodoc:
-
81
templates = lookup_context.find_all(name, Array(paths))
-
81
if templates.empty?
-
1
raise ActionView::MissingTemplate.new([paths], name, [paths], false, 'mailer')
-
else
-
100
templates.uniq { |t| t.formats }.each(&block)
-
end
-
end
-
-
1
def create_parts_from_responses(m, responses) #:nodoc:
-
117
if responses.size == 1 && !m.has_attachments?
-
262
responses[0].each { |k,v| m[k] = v }
-
30
elsif responses.size > 1 && m.has_attachments?
-
4
container = Mail::Part.new
-
4
container.content_type = "multipart/alternative"
-
12
responses.each { |r| insert_part(container, r, m.charset) }
-
4
m.add_part(container)
-
else
-
70
responses.each { |r| insert_part(m, r, m.charset) }
-
end
-
end
-
-
1
def insert_part(container, response, charset) #:nodoc:
-
52
response[:charset] ||= charset
-
52
part = Mail::Part.new(response)
-
52
container.add_part(part)
-
end
-
-
1
ActiveSupport.run_load_hooks(:action_mailer, self)
-
end
-
end
-
1
require 'abstract_controller/collector'
-
1
require 'active_support/core_ext/hash/reverse_merge'
-
1
require 'active_support/core_ext/array/extract_options'
-
-
1
module ActionMailer
-
1
class Collector
-
1
include AbstractController::Collector
-
1
attr_reader :responses
-
-
1
def initialize(context, &block)
-
20
@context = context
-
20
@responses = []
-
20
@default_render = block
-
end
-
-
1
def any(*args, &block)
-
1
options = args.extract_options!
-
1
raise ArgumentError, "You have to supply at least one format" if args.empty?
-
3
args.each { |type| send(type, options.dup, &block) }
-
end
-
1
alias :all :any
-
-
1
def custom(mime, options={})
-
33
options.reverse_merge!(content_type: mime.to_s)
-
33
@context.formats = [mime.to_sym]
-
33
options[:body] = block_given? ? yield : @default_render.call
-
33
@responses << options
-
end
-
end
-
end
-
1
require 'tmpdir'
-
-
1
module ActionMailer
-
# This module handles everything related to mail delivery, from registering
-
# new delivery methods to configuring the mail object to be sent.
-
1
module DeliveryMethods
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
1
class_attribute :delivery_methods, :delivery_method
-
-
# Do not make this inheritable, because we always want it to propagate
-
1
cattr_accessor :raise_delivery_errors
-
1
self.raise_delivery_errors = true
-
-
1
cattr_accessor :perform_deliveries
-
1
self.perform_deliveries = true
-
-
1
self.delivery_methods = {}.freeze
-
1
self.delivery_method = :smtp
-
-
1
add_delivery_method :smtp, Mail::SMTP,
-
address: "localhost",
-
port: 25,
-
domain: 'localhost.localdomain',
-
user_name: nil,
-
password: nil,
-
authentication: nil,
-
enable_starttls_auto: true
-
-
1
add_delivery_method :file, Mail::FileDelivery,
-
location: defined?(Rails.root) ? "#{Rails.root}/tmp/mails" : "#{Dir.tmpdir}/mails"
-
-
1
add_delivery_method :sendmail, Mail::Sendmail,
-
location: '/usr/sbin/sendmail',
-
arguments: '-i -t'
-
-
1
add_delivery_method :test, Mail::TestMailer
-
end
-
-
1
module ClassMethods
-
# Provides a list of emails that have been delivered by Mail::TestMailer
-
1
delegate :deliveries, :deliveries=, to: Mail::TestMailer
-
-
# Adds a new delivery method through the given class using the given
-
# symbol as alias and the default options supplied.
-
#
-
# add_delivery_method :sendmail, Mail::Sendmail,
-
# location: '/usr/sbin/sendmail',
-
# arguments: '-i -t'
-
1
def add_delivery_method(symbol, klass, default_options={})
-
10
class_attribute(:"#{symbol}_settings") unless respond_to?(:"#{symbol}_settings")
-
10
send(:"#{symbol}_settings=", default_options)
-
10
self.delivery_methods = delivery_methods.merge(symbol.to_sym => klass).freeze
-
end
-
-
1
def wrap_delivery_behavior(mail, method=nil, options=nil) # :nodoc:
-
119
method ||= self.delivery_method
-
119
mail.delivery_handler = self
-
-
119
case method
-
when NilClass
-
raise "Delivery method cannot be nil"
-
when Symbol
-
115
if klass = delivery_methods[method]
-
114
mail.delivery_method(klass,(send(:"#{method}_settings") || {}).merge!(options || {}))
-
else
-
1
raise "Invalid delivery method #{method.inspect}"
-
end
-
else
-
4
mail.delivery_method(method)
-
end
-
-
118
mail.perform_deliveries = perform_deliveries
-
118
mail.raise_delivery_errors = raise_delivery_errors
-
end
-
end
-
-
1
def wrap_delivery_behavior!(*args) # :nodoc:
-
119
self.class.wrap_delivery_behavior(message, *args)
-
end
-
end
-
end
-
1
module ActionMailer
-
1
class LogSubscriber < ActiveSupport::LogSubscriber
-
1
def deliver(event)
-
1
return unless logger.info?
-
1
recipients = Array(event.payload[:to]).join(', ')
-
1
info("\nSent mail to #{recipients} (#{event.duration.round(1)}ms)")
-
1
debug(event.payload[:mail])
-
end
-
-
1
def receive(event)
-
1
return unless logger.info?
-
1
info("\nReceived mail (#{event.duration.round(1)}ms)")
-
1
debug(event.payload[:mail])
-
end
-
-
1
def logger
-
84
ActionMailer::Base.logger
-
end
-
end
-
end
-
-
1
ActionMailer::LogSubscriber.attach_to :action_mailer
-
1
module ActionMailer
-
1
module MailHelper
-
# Take the text and format it, indented two spaces for each line, and
-
# wrapped at 72 columns.
-
1
def block_format(text)
-
2
formatted = text.split(/\n\r?\n/).collect { |paragraph|
-
4
format_paragraph(paragraph)
-
}.join("\n\n")
-
-
# Make list points stand on their own line
-
5
formatted.gsub!(/[ ]*([*]+) ([^*]*)/) { |s| " #{$1} #{$2.strip}\n" }
-
2
formatted.gsub!(/[ ]*([#]+) ([^#]*)/) { |s| " #{$1} #{$2.strip}\n" }
-
-
2
formatted
-
end
-
-
# Access the mailer instance.
-
1
def mailer
-
1
@_controller
-
end
-
-
# Access the message instance.
-
1
def message
-
1
@_message
-
end
-
-
# Access the message attachments list.
-
1
def attachments
-
1
@_message.attachments
-
end
-
-
# Returns +text+ wrapped at +len+ columns and indented +indent+ spaces.
-
#
-
# my_text = 'Here is a sample text with more than 40 characters'
-
#
-
# format_paragraph(my_text, 25, 4)
-
# # => " Here is a sample text with\n more than 40 characters"
-
1
def format_paragraph(text, len = 72, indent = 2)
-
6
sentences = [[]]
-
-
6
text.split.each do |word|
-
94
if sentences.first.present? && (sentences.last + [word]).join(' ').length > len
-
9
sentences << [word]
-
else
-
85
sentences.last << word
-
end
-
end
-
-
6
sentences.map { |sentence|
-
15
"#{" " * indent}#{sentence.join(' ')}"
-
}.join "\n"
-
end
-
end
-
end
-
1
require 'delegate'
-
-
1
module ActionMailer
-
1
class QueuedMessage < ::Delegator
-
1
attr_reader :queue
-
-
1
def initialize(queue, mailer_class, method_name, *args)
-
122
@queue = queue
-
122
@job = DeliveryJob.new(mailer_class, method_name, args)
-
end
-
-
1
def __getobj__
-
207
@job.message
-
end
-
-
# Queues the message for delivery.
-
1
def deliver
-
82
tap { @queue.push @job }
-
end
-
-
1
class DeliveryJob
-
1
def initialize(mailer_class, method_name, args)
-
122
@mailer_class = mailer_class
-
122
@method_name = method_name
-
122
@args = args
-
end
-
-
1
def message
-
248
@message ||= @mailer_class.send(:new, @method_name, *@args).message
-
end
-
-
1
def run
-
41
message.deliver
-
end
-
end
-
end
-
end
-
1
require 'active_support/test_case'
-
-
1
module ActionMailer
-
1
class NonInferrableMailerError < ::StandardError
-
1
def initialize(name)
-
super "Unable to determine the mailer to test from #{name}. " +
-
"You'll need to specify it using tests YourMailer in your " +
-
1
"test case definition"
-
end
-
end
-
-
1
class TestCase < ActiveSupport::TestCase
-
-
# Use AM::TestCase for the base class when describing a mailer
-
1
register_spec_type(self) do |desc|
-
6
Class === desc && desc < ActionMailer::Base
-
end
-
1
register_spec_type(/Mailer( ?Test)?\z/i, self)
-
-
1
module Behavior
-
1
extend ActiveSupport::Concern
-
-
1
include ActiveSupport::Testing::ConstantLookup
-
1
include TestHelper
-
-
1
included do
-
1
class_attribute :_mailer_class
-
1
setup :initialize_test_deliveries
-
1
setup :set_expected_mail
-
end
-
-
1
module ClassMethods
-
1
def tests(mailer)
-
23
case mailer
-
when String, Symbol
-
10
self._mailer_class = mailer.to_s.camelize.constantize
-
when Module
-
13
self._mailer_class = mailer
-
else
-
raise NonInferrableMailerError.new(mailer)
-
end
-
end
-
-
1
def mailer_class
-
22
if mailer = self._mailer_class
-
13
mailer
-
else
-
9
tests determine_default_mailer(name)
-
end
-
end
-
-
1
def determine_default_mailer(name)
-
10
mailer = determine_constant_from_test_name(name) do |constant|
-
9
Class === constant && constant < ActionMailer::Base
-
end
-
10
raise NonInferrableMailerError.new(name) if mailer.nil?
-
9
mailer
-
end
-
end
-
-
1
protected
-
-
1
def initialize_test_deliveries
-
43
ActionMailer::Base.delivery_method = :test
-
43
ActionMailer::Base.perform_deliveries = true
-
43
ActionMailer::Base.deliveries.clear
-
end
-
-
1
def set_expected_mail
-
43
@expected = Mail.new
-
43
@expected.content_type ["text", "plain", { "charset" => charset }]
-
43
@expected.mime_version = '1.0'
-
end
-
-
1
private
-
-
1
def charset
-
44
"UTF-8"
-
end
-
-
1
def encode(subject)
-
Mail::Encodings.q_value_encode(subject, charset)
-
end
-
-
1
def read_fixture(action)
-
IO.readlines(File.join(Rails.root, 'test', 'fixtures', self.class.mailer_class.name.underscore, action))
-
end
-
end
-
-
1
include Behavior
-
end
-
end
-
1
module ActionMailer
-
1
module TestHelper
-
# Asserts that the number of emails sent matches the given number.
-
#
-
# def test_emails
-
# assert_emails 0
-
# ContactMailer.deliver_contact
-
# assert_emails 1
-
# ContactMailer.deliver_contact
-
# assert_emails 2
-
# end
-
#
-
# If a block is passed, that block should cause the specified number of
-
# emails to be sent.
-
#
-
# def test_emails_again
-
# assert_emails 1 do
-
# ContactMailer.deliver_contact
-
# end
-
#
-
# assert_emails 2 do
-
# ContactMailer.deliver_contact
-
# ContactMailer.deliver_contact
-
# end
-
# end
-
1
def assert_emails(number)
-
9
if block_given?
-
7
original_count = ActionMailer::Base.deliveries.size
-
7
yield
-
7
new_count = ActionMailer::Base.deliveries.size
-
7
assert_equal original_count + number, new_count, "#{number} emails expected, but #{new_count - original_count} were sent"
-
else
-
2
assert_equal number, ActionMailer::Base.deliveries.size
-
end
-
end
-
-
# Assert that no emails have been sent.
-
#
-
# def test_emails
-
# assert_no_emails
-
# ContactMailer.deliver_contact
-
# assert_emails 1
-
# end
-
#
-
# If a block is passed, that block should not cause any emails to be sent.
-
#
-
# def test_emails_again
-
# assert_no_emails do
-
# # No emails should be sent from this block
-
# end
-
# end
-
#
-
# Note: This assertion is simply a shortcut for:
-
#
-
# assert_emails 0
-
1
def assert_no_emails(&block)
-
2
assert_emails 0, &block
-
end
-
end
-
end
-
1
module ActionMailer
-
1
module VERSION #:nodoc:
-
1
MAJOR = 4
-
1
MINOR = 0
-
1
TINY = 0
-
1
PRE = "beta"
-
-
1
STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
-
end
-
end
-
1
require 'action_pack'
-
1
require 'active_support/rails'
-
1
require 'active_support/core_ext/module/attr_internal'
-
1
require 'active_support/core_ext/module/anonymous'
-
1
require 'active_support/i18n'
-
-
1
module AbstractController
-
1
extend ActiveSupport::Autoload
-
-
1
autoload :Base
-
1
autoload :Callbacks
-
1
autoload :Collector
-
1
autoload :Helpers
-
1
autoload :Layouts
-
1
autoload :Logger
-
1
autoload :Rendering
-
1
autoload :Translation
-
1
autoload :AssetPaths
-
1
autoload :ViewPaths
-
1
autoload :UrlFor
-
end
-
1
module AbstractController
-
1
module AssetPaths #:nodoc:
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
2
config_accessor :asset_host, :assets_dir, :javascripts_dir,
-
:stylesheets_dir, :default_asset_host_protocol, :relative_url_root
-
end
-
end
-
end
-
1
require 'erubis'
-
1
require 'set'
-
1
require 'active_support/configurable'
-
1
require 'active_support/descendants_tracker'
-
1
require 'active_support/core_ext/module/anonymous'
-
-
1
module AbstractController
-
1
class Error < StandardError #:nodoc:
-
end
-
-
1
class ActionNotFound < StandardError #:nodoc:
-
end
-
-
# <tt>AbstractController::Base</tt> is a low-level API. Nobody should be
-
# using it directly, and subclasses (like ActionController::Base) are
-
# expected to provide their own +render+ method, since rendering means
-
# different things depending on the context.
-
1
class Base
-
1
attr_internal :response_body
-
1
attr_internal :action_name
-
1
attr_internal :formats
-
-
1
include ActiveSupport::Configurable
-
1
extend ActiveSupport::DescendantsTracker
-
-
1
undef_method :not_implemented
-
1
class << self
-
1
attr_reader :abstract
-
1
alias_method :abstract?, :abstract
-
-
# Define a controller as abstract. See internal_methods for more
-
# details.
-
1
def abstract!
-
4
@abstract = true
-
end
-
-
1
def inherited(klass) # :nodoc:
-
# define the abstract ivar on subclasses so that we don't get
-
# uninitialized ivar warnings
-
27
unless klass.instance_variable_defined?(:@abstract)
-
27
klass.instance_variable_set(:@abstract, false)
-
end
-
27
super
-
end
-
-
# A list of all internal methods for a controller. This finds the first
-
# abstract superclass of a controller, and gets a list of all public
-
# instance methods on that abstract class. Public instance methods of
-
# a controller would normally be considered action methods, so methods
-
# declared on abstract classes are being removed.
-
# (ActionController::Metal and ActionController::Base are defined as abstract)
-
1
def internal_methods
-
34
controller = self
-
-
34
controller = controller.superclass until controller.abstract?
-
34
controller.public_instance_methods(true)
-
end
-
-
# The list of hidden actions. Defaults to an empty array.
-
# This can be modified by other modules or subclasses
-
# to specify particular actions as hidden.
-
#
-
# ==== Returns
-
# * <tt>Array</tt> - An array of method names that should not be considered actions.
-
1
def hidden_actions
-
33
[]
-
end
-
-
# A list of method names that should be considered actions. This
-
# includes all public instance methods on a controller, less
-
# any internal methods (see #internal_methods), adding back in
-
# any methods that are internal, but still exist on the class
-
# itself. Finally, #hidden_actions are removed.
-
#
-
# ==== Returns
-
# * <tt>Set</tt> - A set of all methods that should be considered actions.
-
1
def action_methods
-
@action_methods ||= begin
-
# All public instance methods of this class, including ancestors
-
34
methods = (public_instance_methods(true) -
-
# Except for public instance methods of Base and its ancestors
-
internal_methods +
-
# Be sure to include shadowed public instance methods of this class
-
279
public_instance_methods(false)).uniq.map { |x| x.to_s } -
-
# And always exclude explicitly hidden actions
-
hidden_actions.to_a
-
-
# Clear out AS callback method pollution
-
279
Set.new(methods.reject { |method| method =~ /_one_time_conditions/ })
-
279
end
-
end
-
-
# action_methods are cached and there is sometimes need to refresh
-
# them. clear_action_methods! allows you to do that, so next time
-
# you run action_methods, they will be recalculated
-
1
def clear_action_methods!
-
320
@action_methods = nil
-
end
-
-
# Returns the full controller name, underscored, without the ending Controller.
-
# For instance, MyApp::MyPostsController would return "my_app/my_posts" for
-
# controller_path.
-
#
-
# ==== Returns
-
# * <tt>String</tt>
-
1
def controller_path
-
9
@controller_path ||= name.sub(/Controller$/, '').underscore unless anonymous?
-
end
-
-
# Refresh the cached action_methods when a new action_method is added.
-
1
def method_added(name)
-
320
super
-
320
clear_action_methods!
-
end
-
end
-
-
1
abstract!
-
-
# Calls the action going through the entire action dispatch stack.
-
#
-
# The actual method that is called is determined by calling
-
# #method_for_action. If no method can handle the action, then an
-
# ActionNotFound error is raised.
-
#
-
# ==== Returns
-
# * <tt>self</tt>
-
1
def process(action, *args)
-
121
@_action_name = action_name = action.to_s
-
-
121
unless action_name = method_for_action(action_name)
-
raise ActionNotFound, "The action '#{action}' could not be found for #{self.class.name}"
-
end
-
-
121
@_response_body = nil
-
-
121
process_action(action_name, *args)
-
end
-
-
# Delegates to the class' #controller_path
-
1
def controller_path
-
121
self.class.controller_path
-
end
-
-
# Delegates to the class' #action_methods
-
1
def action_methods
-
self.class.action_methods
-
end
-
-
# Returns true if a method for the action is available and
-
# can be dispatched, false otherwise.
-
#
-
# Notice that <tt>action_methods.include?("foo")</tt> may return
-
# false and <tt>available_action?("foo")</tt> returns true because
-
# this method considers actions that are also available
-
# through other means, for example, implicit render ones.
-
#
-
# ==== Parameters
-
# * <tt>action_name</tt> - The name of an action to be tested
-
#
-
# ==== Returns
-
# * <tt>TrueClass</tt>, <tt>FalseClass</tt>
-
1
def available_action?(action_name)
-
method_for_action(action_name).present?
-
end
-
-
1
private
-
-
# Returns true if the name can be considered an action because
-
# it has a method defined in the controller.
-
#
-
# ==== Parameters
-
# * <tt>name</tt> - The name of an action to be tested
-
#
-
# ==== Returns
-
# * <tt>TrueClass</tt>, <tt>FalseClass</tt>
-
#
-
# :api: private
-
1
def action_method?(name)
-
121
self.class.action_methods.include?(name)
-
end
-
-
# Call the action. Override this in a subclass to modify the
-
# behavior around processing an action. This, and not #process,
-
# is the intended way to override action dispatching.
-
#
-
# Notice that the first argument is the method to be dispatched
-
# which is *not* necessarily the same as the action name.
-
1
def process_action(method_name, *args)
-
121
send_action(method_name, *args)
-
end
-
-
# Actually call the method associated with the action. Override
-
# this method if you wish to change how action methods are called,
-
# not to add additional behavior around it. For example, you would
-
# override #send_action if you want to inject arguments into the
-
# method.
-
1
alias send_action send
-
-
# If the action name was not found, but a method called "action_missing"
-
# was found, #method_for_action will return "_handle_action_missing".
-
# This method calls #action_missing with the current action name.
-
1
def _handle_action_missing(*args)
-
action_missing(@_action_name, *args)
-
end
-
-
# Takes an action name and returns the name of the method that will
-
# handle the action. In normal cases, this method returns the same
-
# name as it receives. By default, if #method_for_action receives
-
# a name that is not an action, it will look for an #action_missing
-
# method and return "_handle_action_missing" if one is found.
-
#
-
# Subclasses may override this method to add additional conditions
-
# that should be considered an action. For instance, an HTTP controller
-
# with a template matching the action name is considered to exist.
-
#
-
# If you override this method to handle additional cases, you may
-
# also provide a method (like _handle_method_missing) to handle
-
# the case.
-
#
-
# If none of these conditions are true, and method_for_action
-
# returns nil, an ActionNotFound exception will be raised.
-
#
-
# ==== Parameters
-
# * <tt>action_name</tt> - An action name to find a method name for
-
#
-
# ==== Returns
-
# * <tt>string</tt> - The name of the method that handles the action
-
# * <tt>nil</tt> - No method name could be found. Raise ActionNotFound.
-
1
def method_for_action(action_name)
-
121
if action_method?(action_name)
-
121
action_name
-
elsif respond_to?(:action_missing, true)
-
"_handle_action_missing"
-
end
-
end
-
end
-
end
-
1
module AbstractController
-
1
module Callbacks
-
1
extend ActiveSupport::Concern
-
-
# Uses ActiveSupport::Callbacks as the base functionality. For
-
# more details on the whole callback system, read the documentation
-
# for ActiveSupport::Callbacks.
-
1
include ActiveSupport::Callbacks
-
-
1
included do
-
2
define_callbacks :process_action, :terminator => "response_body", :skip_after_callbacks_if_terminated => true
-
end
-
-
# Override AbstractController::Base's process_action to run the
-
# process_action callbacks around the normal behavior.
-
1
def process_action(*args)
-
121
run_callbacks(:process_action) do
-
121
super
-
end
-
end
-
-
1
module ClassMethods
-
# If :only or :except are used, convert the options into the
-
# :unless and :if options of ActiveSupport::Callbacks.
-
# The basic idea is that :only => :index gets converted to
-
# :if => proc {|c| c.action_name == "index" }.
-
#
-
# ==== Options
-
# * <tt>only</tt> - The callback should be run only for this action
-
# * <tt>except</tt> - The callback should be run for all actions except this action
-
1
def _normalize_callback_options(options)
-
3
_normalize_callback_option(options, :only, :if)
-
3
_normalize_callback_option(options, :except, :unless)
-
end
-
-
1
def _normalize_callback_option(options, from, to) # :nodoc:
-
6
if from = options[from]
-
from = Array(from).map {|o| "action_name == '#{o}'"}.join(" || ")
-
options[to] = Array(options[to]) << from
-
end
-
end
-
-
# Skip before, after, and around filters matching any of the names
-
#
-
# ==== Parameters
-
# * <tt>names</tt> - A list of valid names that could be used for
-
# callbacks. Note that skipping uses Ruby equality, so it's
-
# impossible to skip a callback defined using an anonymous proc
-
# using #skip_filter
-
1
def skip_filter(*names)
-
skip_before_filter(*names)
-
skip_after_filter(*names)
-
skip_around_filter(*names)
-
end
-
-
# Take callback names and an optional callback proc, normalize them,
-
# then call the block with each callback. This allows us to abstract
-
# the normalization across several methods that use it.
-
#
-
# ==== Parameters
-
# * <tt>callbacks</tt> - An array of callbacks, with an optional
-
# options hash as the last parameter.
-
# * <tt>block</tt> - A proc that should be added to the callbacks.
-
#
-
# ==== Block Parameters
-
# * <tt>name</tt> - The callback to be added
-
# * <tt>options</tt> - A hash of options to be used when adding the callback
-
1
def _insert_callbacks(callbacks, block = nil)
-
3
options = callbacks.last.is_a?(Hash) ? callbacks.pop : {}
-
3
_normalize_callback_options(options)
-
3
callbacks.push(block) if block
-
3
callbacks.each do |callback|
-
3
yield callback, options
-
end
-
end
-
-
##
-
# :method: before_filter
-
#
-
# :call-seq: before_filter(names, block)
-
#
-
# Append a before filter. See _insert_callbacks for parameter details.
-
-
##
-
# :method: prepend_before_filter
-
#
-
# :call-seq: prepend_before_filter(names, block)
-
#
-
# Prepend a before filter. See _insert_callbacks for parameter details.
-
-
##
-
# :method: skip_before_filter
-
#
-
# :call-seq: skip_before_filter(names)
-
#
-
# Skip a before filter. See _insert_callbacks for parameter details.
-
-
##
-
# :method: append_before_filter
-
#
-
# :call-seq: append_before_filter(names, block)
-
#
-
# Append a before filter. See _insert_callbacks for parameter details.
-
-
##
-
# :method: after_filter
-
#
-
# :call-seq: after_filter(names, block)
-
#
-
# Append an after filter. See _insert_callbacks for parameter details.
-
-
##
-
# :method: prepend_after_filter
-
#
-
# :call-seq: prepend_after_filter(names, block)
-
#
-
# Prepend an after filter. See _insert_callbacks for parameter details.
-
-
##
-
# :method: skip_after_filter
-
#
-
# :call-seq: skip_after_filter(names)
-
#
-
# Skip an after filter. See _insert_callbacks for parameter details.
-
-
##
-
# :method: append_after_filter
-
#
-
# :call-seq: append_after_filter(names, block)
-
#
-
# Append an after filter. See _insert_callbacks for parameter details.
-
-
##
-
# :method: around_filter
-
#
-
# :call-seq: around_filter(names, block)
-
#
-
# Append an around filter. See _insert_callbacks for parameter details.
-
-
##
-
# :method: prepend_around_filter
-
#
-
# :call-seq: prepend_around_filter(names, block)
-
#
-
# Prepend an around filter. See _insert_callbacks for parameter details.
-
-
##
-
# :method: skip_around_filter
-
#
-
# :call-seq: skip_around_filter(names)
-
#
-
# Skip an around filter. See _insert_callbacks for parameter details.
-
-
##
-
# :method: append_around_filter
-
#
-
# :call-seq: append_around_filter(names, block)
-
#
-
# Append an around filter. See _insert_callbacks for parameter details.
-
-
# set up before_filter, prepend_before_filter, skip_before_filter, etc.
-
# for each of before, after, and around.
-
1
[:before, :after, :around].each do |filter|
-
3
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
-
# Append a before, after or around filter. See _insert_callbacks
-
# for details on the allowed parameters.
-
def #{filter}_filter(*names, &blk) # def before_filter(*names, &blk)
-
_insert_callbacks(names, blk) do |name, options| # _insert_callbacks(names, blk) do |name, options|
-
set_callback(:process_action, :#{filter}, name, options) # set_callback(:process_action, :before, name, options)
-
end # end
-
end # end
-
-
# Prepend a before, after or around filter. See _insert_callbacks
-
# for details on the allowed parameters.
-
def prepend_#{filter}_filter(*names, &blk) # def prepend_before_filter(*names, &blk)
-
_insert_callbacks(names, blk) do |name, options| # _insert_callbacks(names, blk) do |name, options|
-
set_callback(:process_action, :#{filter}, name, options.merge(:prepend => true)) # set_callback(:process_action, :before, name, options.merge(:prepend => true))
-
end # end
-
end # end
-
-
# Skip a before, after or around filter. See _insert_callbacks
-
# for details on the allowed parameters.
-
def skip_#{filter}_filter(*names) # def skip_before_filter(*names)
-
_insert_callbacks(names) do |name, options| # _insert_callbacks(names) do |name, options|
-
skip_callback(:process_action, :#{filter}, name, options) # skip_callback(:process_action, :before, name, options)
-
end # end
-
end # end
-
-
# *_filter is the same as append_*_filter
-
alias_method :append_#{filter}_filter, :#{filter}_filter # alias_method :append_before_filter, :before_filter
-
RUBY_EVAL
-
end
-
end
-
end
-
end
-
1
require "action_dispatch/http/mime_type"
-
-
1
module AbstractController
-
1
module Collector
-
1
def self.generate_method_for_mime(mime)
-
21
sym = mime.is_a?(Symbol) ? mime : mime.to_sym
-
21
const = sym.upcase
-
21
class_eval <<-RUBY, __FILE__, __LINE__ + 1
-
def #{sym}(*args, &block) # def html(*args, &block)
-
custom(Mime::#{const}, *args, &block) # custom(Mime::HTML, *args, &block)
-
end # end
-
RUBY
-
end
-
-
1
Mime::SET.each do |mime|
-
21
generate_method_for_mime(mime)
-
end
-
-
1
Mime::Type.register_callback do |mime|
-
generate_method_for_mime(mime) unless self.instance_methods.include?(mime.to_sym)
-
end
-
-
1
protected
-
-
1
def method_missing(symbol, &block)
-
mime_constant = Mime.const_get(symbol.upcase)
-
-
if Mime::SET.include?(mime_constant)
-
AbstractController::Collector.generate_method_for_mime(mime_constant)
-
send(symbol, &block)
-
else
-
super
-
end
-
end
-
end
-
end
-
1
require 'active_support/dependencies'
-
-
1
module AbstractController
-
1
module Helpers
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
2
class_attribute :_helpers
-
2
self._helpers = Module.new
-
-
2
class_attribute :_helper_methods
-
2
self._helper_methods = Array.new
-
end
-
-
1
module ClassMethods
-
# When a class is inherited, wrap its helper module in a new module.
-
# This ensures that the parent class's module can be changed
-
# independently of the child class's.
-
1
def inherited(klass)
-
24
helpers = _helpers
-
48
klass._helpers = Module.new { include helpers }
-
48
klass.class_eval { default_helper_module! unless anonymous? }
-
24
super
-
end
-
-
# Declare a controller method as a helper. For example, the following
-
# makes the +current_user+ controller method available to the view:
-
# class ApplicationController < ActionController::Base
-
# helper_method :current_user, :logged_in?
-
#
-
# def current_user
-
# @current_user ||= User.find_by_id(session[:user])
-
# end
-
#
-
# def logged_in?
-
# current_user != nil
-
# end
-
# end
-
#
-
# In a view:
-
# <% if logged_in? -%>Welcome, <%= current_user.name %><% end -%>
-
#
-
# ==== Parameters
-
# * <tt>method[, method]</tt> - A name or names of a method on the controller
-
# to be made available on the view.
-
1
def helper_method(*meths)
-
5
meths.flatten!
-
5
self._helper_methods += meths
-
-
5
meths.each do |meth|
-
5
_helpers.class_eval <<-ruby_eval, __FILE__, __LINE__ + 1
-
def #{meth}(*args, &blk) # def current_user(*args, &blk)
-
controller.send(%(#{meth}), *args, &blk) # controller.send(:current_user, *args, &blk)
-
end # end
-
ruby_eval
-
end
-
end
-
-
# The +helper+ class method can take a series of helper module names, a block, or both.
-
#
-
# ==== Parameters
-
# * <tt>*args</tt> - Module, Symbol, String, :all
-
# * <tt>block</tt> - A block defining helper methods
-
#
-
# ==== Examples
-
# When the argument is a module it will be included directly in the template class.
-
# helper FooHelper # => includes FooHelper
-
#
-
# When the argument is a string or symbol, the method will provide the "_helper" suffix, require the file
-
# and include the module in the template class. The second form illustrates how to include custom helpers
-
# when working with namespaced controllers, or other cases where the file containing the helper definition is not
-
# in one of Rails' standard load paths:
-
# helper :foo # => requires 'foo_helper' and includes FooHelper
-
# helper 'resources/foo' # => requires 'resources/foo_helper' and includes Resources::FooHelper
-
#
-
# Additionally, the +helper+ class method can receive and evaluate a block, making the methods defined available
-
# to the template.
-
#
-
# # One line
-
# helper { def hello() "Hello, world!" end }
-
#
-
# # Multi-line
-
# helper do
-
# def foo(bar)
-
# "#{bar} is the very best"
-
# end
-
# end
-
#
-
# Finally, all the above styles can be mixed together, and the +helper+ method can be invoked with a mix of
-
# +symbols+, +strings+, +modules+ and blocks.
-
#
-
# helper(:three, BlindHelper) { def mice() 'mice' end }
-
#
-
1
def helper(*args, &block)
-
24
modules_for_helpers(args).each do |mod|
-
1
add_template_helper(mod)
-
end
-
-
1
_helpers.module_eval(&block) if block_given?
-
end
-
-
# Clears up all existing helpers in this class, only keeping the helper
-
# with the same name as this class.
-
1
def clear_helpers
-
inherited_helper_methods = _helper_methods
-
self._helpers = Module.new
-
self._helper_methods = Array.new
-
-
inherited_helper_methods.each { |meth| helper_method meth }
-
default_helper_module! unless anonymous?
-
end
-
-
# Returns a list of modules, normalized from the acceptable kinds of
-
# helpers with the following behavior:
-
#
-
# String or Symbol:: :FooBar or "FooBar" becomes "foo_bar_helper",
-
# and "foo_bar_helper.rb" is loaded using require_dependency.
-
#
-
# Module:: No further processing
-
#
-
# After loading the appropriate files, the corresponding modules
-
# are returned.
-
#
-
# ==== Parameters
-
# * <tt>args</tt> - An array of helpers
-
#
-
# ==== Returns
-
# * <tt>Array</tt> - A normalized list of modules for the list of
-
# helpers provided.
-
1
def modules_for_helpers(args)
-
24
args.flatten.map! do |arg|
-
24
case arg
-
when String, Symbol
-
23
file_name = "#{arg.to_s.underscore}_helper"
-
23
begin
-
23
require_dependency(file_name)
-
rescue LoadError => e
-
23
raise MissingHelperError.new(e, file_name)
-
end
-
file_name.camelize.constantize
-
when Module
-
1
arg
-
else
-
raise ArgumentError, "helper must be a String, Symbol, or Module"
-
end
-
end
-
end
-
-
1
class MissingHelperError < LoadError
-
1
def initialize(error, path)
-
23
@error = error
-
23
@path = "helpers/#{path}.rb"
-
23
set_backtrace error.backtrace
-
23
super("Missing helper file helpers/%s.rb" % path)
-
end
-
end
-
-
1
private
-
# Makes all the (instance) methods in the helper module available to templates
-
# rendered through this controller.
-
#
-
# ==== Parameters
-
# * <tt>module</tt> - The module to include into the current helper module
-
# for the class
-
1
def add_template_helper(mod)
-
2
_helpers.module_eval { include mod }
-
end
-
-
1
def default_helper_module!
-
23
module_name = name.sub(/Controller$/, '')
-
23
module_path = module_name.underscore
-
23
helper module_path
-
rescue MissingSourceFile => e
-
23
raise e unless e.is_missing? "helpers/#{module_path}_helper"
-
rescue NameError => e
-
raise e unless e.missing_name? "#{module_name}Helper"
-
end
-
end
-
end
-
end
-
1
require "active_support/core_ext/module/remove_method"
-
-
1
module AbstractController
-
# Layouts reverse the common pattern of including shared headers and footers in many templates to isolate changes in
-
# repeated setups. The inclusion pattern has pages that look like this:
-
#
-
# <%= render "shared/header" %>
-
# Hello World
-
# <%= render "shared/footer" %>
-
#
-
# This approach is a decent way of keeping common structures isolated from the changing content, but it's verbose
-
# and if you ever want to change the structure of these two includes, you'll have to change all the templates.
-
#
-
# With layouts, you can flip it around and have the common structure know where to insert changing content. This means
-
# that the header and footer are only mentioned in one place, like this:
-
#
-
# // The header part of this layout
-
# <%= yield %>
-
# // The footer part of this layout
-
#
-
# And then you have content pages that look like this:
-
#
-
# hello world
-
#
-
# At rendering time, the content page is computed and then inserted in the layout, like this:
-
#
-
# // The header part of this layout
-
# hello world
-
# // The footer part of this layout
-
#
-
# == Accessing shared variables
-
#
-
# Layouts have access to variables specified in the content pages and vice versa. This allows you to have layouts with
-
# references that won't materialize before rendering time:
-
#
-
# <h1><%= @page_title %></h1>
-
# <%= yield %>
-
#
-
# ...and content pages that fulfill these references _at_ rendering time:
-
#
-
# <% @page_title = "Welcome" %>
-
# Off-world colonies offers you a chance to start a new life
-
#
-
# The result after rendering is:
-
#
-
# <h1>Welcome</h1>
-
# Off-world colonies offers you a chance to start a new life
-
#
-
# == Layout assignment
-
#
-
# You can either specify a layout declaratively (using the #layout class method) or give
-
# it the same name as your controller, and place it in <tt>app/views/layouts</tt>.
-
# If a subclass does not have a layout specified, it inherits its layout using normal Ruby inheritance.
-
#
-
# For instance, if you have PostsController and a template named <tt>app/views/layouts/posts.html.erb</tt>,
-
# that template will be used for all actions in PostsController and controllers inheriting
-
# from PostsController.
-
#
-
# If you use a module, for instance Weblog::PostsController, you will need a template named
-
# <tt>app/views/layouts/weblog/posts.html.erb</tt>.
-
#
-
# Since all your controllers inherit from ApplicationController, they will use
-
# <tt>app/views/layouts/application.html.erb</tt> if no other layout is specified
-
# or provided.
-
#
-
# == Inheritance Examples
-
#
-
# class BankController < ActionController::Base
-
# # bank.html.erb exists
-
#
-
# class ExchangeController < BankController
-
# # exchange.html.erb exists
-
#
-
# class CurrencyController < BankController
-
#
-
# class InformationController < BankController
-
# layout "information"
-
#
-
# class TellerController < InformationController
-
# # teller.html.erb exists
-
#
-
# class EmployeeController < InformationController
-
# # employee.html.erb exists
-
# layout nil
-
#
-
# class VaultController < BankController
-
# layout :access_level_layout
-
#
-
# class TillController < BankController
-
# layout false
-
#
-
# In these examples, we have three implicit lookup scenarios:
-
# * The BankController uses the "bank" layout.
-
# * The ExchangeController uses the "exchange" layout.
-
# * The CurrencyController inherits the layout from BankController.
-
#
-
# However, when a layout is explicitly set, the explicitly set layout wins:
-
# * The InformationController uses the "information" layout, explicitly set.
-
# * The TellerController also uses the "information" layout, because the parent explicitly set it.
-
# * The EmployeeController uses the "employee" layout, because it set the layout to nil, resetting the parent configuration.
-
# * The VaultController chooses a layout dynamically by calling the <tt>access_level_layout</tt> method.
-
# * The TillController does not use a layout at all.
-
#
-
# == Types of layouts
-
#
-
# Layouts are basically just regular templates, but the name of this template needs not be specified statically. Sometimes
-
# you want to alternate layouts depending on runtime information, such as whether someone is logged in or not. This can
-
# be done either by specifying a method reference as a symbol or using an inline method (as a proc).
-
#
-
# The method reference is the preferred approach to variable layouts and is used like this:
-
#
-
# class WeblogController < ActionController::Base
-
# layout :writers_and_readers
-
#
-
# def index
-
# # fetching posts
-
# end
-
#
-
# private
-
# def writers_and_readers
-
# logged_in? ? "writer_layout" : "reader_layout"
-
# end
-
# end
-
#
-
# Now when a new request for the index action is processed, the layout will vary depending on whether the person accessing
-
# is logged in or not.
-
#
-
# If you want to use an inline method, such as a proc, do something like this:
-
#
-
# class WeblogController < ActionController::Base
-
# layout proc { |controller| controller.logged_in? ? "writer_layout" : "reader_layout" }
-
# end
-
#
-
# If an argument isn't given to the proc, it's evaluated in the context of
-
# the current controller anyway.
-
#
-
# class WeblogController < ActionController::Base
-
# layout proc { logged_in? ? "writer_layout" : "reader_layout" }
-
# end
-
#
-
# Of course, the most common way of specifying a layout is still just as a plain template name:
-
#
-
# class WeblogController < ActionController::Base
-
# layout "weblog_standard"
-
# end
-
#
-
# The template will be looked always in <tt>app/views/layouts/</tt> folder. But you can point
-
# <tt>layouts</tt> folder direct also. <tt>layout "layouts/demo"</tt> is the same as <tt>layout "demo"</tt>.
-
#
-
# Setting the layout to nil forces it to be looked up in the filesystem and fallbacks to the parent behavior if none exists.
-
# Setting it to nil is useful to re-enable template lookup overriding a previous configuration set in the parent:
-
#
-
# class ApplicationController < ActionController::Base
-
# layout "application"
-
# end
-
#
-
# class PostsController < ApplicationController
-
# # Will use "application" layout
-
# end
-
#
-
# class CommentsController < ApplicationController
-
# # Will search for "comments" layout and fallback "application" layout
-
# layout nil
-
# end
-
#
-
# == Conditional layouts
-
#
-
# If you have a layout that by default is applied to all the actions of a controller, you still have the option of rendering
-
# a given action or set of actions without a layout, or restricting a layout to only a single action or a set of actions. The
-
# <tt>:only</tt> and <tt>:except</tt> options can be passed to the layout call. For example:
-
#
-
# class WeblogController < ActionController::Base
-
# layout "weblog_standard", :except => :rss
-
#
-
# # ...
-
#
-
# end
-
#
-
# This will assign "weblog_standard" as the WeblogController's layout for all actions except for the +rss+ action, which will
-
# be rendered directly, without wrapping a layout around the rendered view.
-
#
-
# Both the <tt>:only</tt> and <tt>:except</tt> condition can accept an arbitrary number of method references, so
-
# #<tt>:except => [ :rss, :text_only ]</tt> is valid, as is <tt>:except => :rss</tt>.
-
#
-
# == Using a different layout in the action render call
-
#
-
# If most of your actions use the same layout, it makes perfect sense to define a controller-wide layout as described above.
-
# Sometimes you'll have exceptions where one action wants to use a different layout than the rest of the controller.
-
# You can do this by passing a <tt>:layout</tt> option to the <tt>render</tt> call. For example:
-
#
-
# class WeblogController < ActionController::Base
-
# layout "weblog_standard"
-
#
-
# def help
-
# render :action => "help", :layout => "help"
-
# end
-
# end
-
#
-
# This will override the controller-wide "weblog_standard" layout, and will render the help action with the "help" layout instead.
-
1
module Layouts
-
1
extend ActiveSupport::Concern
-
-
1
include Rendering
-
-
1
included do
-
2
class_attribute :_layout, :_layout_conditions, :instance_accessor => false
-
2
self._layout = nil
-
2
self._layout_conditions = {}
-
2
_write_layout_method
-
end
-
-
1
delegate :_layout_conditions, :to => "self.class"
-
-
1
module ClassMethods
-
1
def inherited(klass)
-
24
super
-
24
klass._write_layout_method
-
end
-
-
# This module is mixed in if layout conditions are provided. This means
-
# that if no layout conditions are used, this method is not used
-
1
module LayoutConditions
-
# Determines whether the current action has a layout by checking the
-
# action name against the :only and :except conditions set on the
-
# layout.
-
#
-
# ==== Returns
-
# * <tt> Boolean</tt> - True if the action has a layout, false otherwise.
-
1
def conditional_layout?
-
3
return unless super
-
-
3
conditions = _layout_conditions
-
-
3
if only = conditions[:only]
-
only.include?(action_name)
-
3
elsif except = conditions[:except]
-
3
!except.include?(action_name)
-
else
-
true
-
end
-
end
-
end
-
-
# Specify the layout to use for this class.
-
#
-
# If the specified layout is a:
-
# String:: the String is the template name
-
# Symbol:: call the method specified by the symbol, which will return the template name
-
# false:: There is no layout
-
# true:: raise an ArgumentError
-
# nil:: Force default layout behavior with inheritance
-
#
-
# ==== Parameters
-
# * <tt>layout</tt> - The layout to use.
-
#
-
# ==== Options (conditions)
-
# * :only - A list of actions to apply this layout to.
-
# * :except - Apply this layout to all actions but this one.
-
1
def layout(layout, conditions = {})
-
1
include LayoutConditions unless conditions.empty?
-
-
3
conditions.each {|k, v| conditions[k] = Array(v).map {|a| a.to_s} }
-
1
self._layout_conditions = conditions
-
-
1
self._layout = layout
-
1
_write_layout_method
-
end
-
-
# If no layout is supplied, look for a template named the return
-
# value of this method.
-
#
-
# ==== Returns
-
# * <tt>String</tt> - A template name
-
1
def _implied_layout_name
-
53
controller_path
-
end
-
-
# Creates a _layout method to be called by _default_layout .
-
#
-
# If a layout is not explicitly mentioned then look for a layout with the controller's name.
-
# if nothing is found then try same procedure to find super class's layout.
-
1
def _write_layout_method
-
27
remove_possible_method(:_layout)
-
-
27
prefixes = _implied_layout_name =~ /\blayouts/ ? [] : ["layouts"]
-
27
name_clause = if name
-
<<-RUBY
-
26
lookup_context.find_all("#{_implied_layout_name}", #{prefixes.inspect}).first || super
-
RUBY
-
else
-
1
<<-RUBY
-
super
-
RUBY
-
end
-
-
27
layout_definition = case _layout
-
when String
-
1
_layout.inspect
-
when Symbol
-
<<-RUBY
-
#{_layout}.tap do |layout|
-
unless layout.is_a?(String) || !layout
-
raise ArgumentError, "Your layout method :#{_layout} returned \#{layout}. It " \
-
"should have returned a String, false, or nil"
-
end
-
end
-
RUBY
-
when Proc
-
define_method :_layout_from_proc, &_layout
-
_layout.arity == 0 ? "_layout_from_proc" : "_layout_from_proc(self)"
-
when false
-
nil
-
when true
-
raise ArgumentError, "Layouts must be specified as a String, Symbol, Proc, false, or nil"
-
when nil
-
26
name_clause
-
end
-
-
27
self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
-
def _layout
-
if conditional_layout?
-
#{layout_definition}
-
else
-
#{name_clause}
-
end
-
end
-
private :_layout
-
RUBY
-
end
-
end
-
-
1
def _normalize_options(options)
-
137
super
-
-
137
if _include_layout?(options)
-
210
layout = options.delete(:layout) { :default }
-
107
options[:layout] = _layout_for_option(layout)
-
end
-
end
-
-
1
attr_internal_writer :action_has_layout
-
-
1
def initialize(*)
-
122
@_action_has_layout = true
-
122
super
-
end
-
-
1
def action_has_layout?
-
103
@_action_has_layout
-
end
-
-
1
def conditional_layout?
-
201
true
-
end
-
-
1
private
-
-
# This will be overwritten by _write_layout_method
-
1
def _layout; end
-
-
# Determine the layout for a given name, taking into account the name type.
-
#
-
# ==== Parameters
-
# * <tt>name</tt> - The name of the template
-
1
def _layout_for_option(name)
-
107
case name
-
3
when String then _normalize_layout(name)
-
when Proc then name
-
when true then Proc.new { _default_layout(true) }
-
206
when :default then Proc.new { _default_layout(false) }
-
when false, nil then nil
-
else
-
raise ArgumentError,
-
"String, Proc, :default, true, or false, expected for `layout'; you passed #{name.inspect}"
-
end
-
end
-
-
1
def _normalize_layout(value)
-
106
value.is_a?(String) && value !~ /\blayouts/ ? "layouts/#{value}" : value
-
end
-
-
# Returns the default layout for this controller.
-
# Optionally raises an exception if the layout could not be found.
-
#
-
# ==== Parameters
-
# * <tt>require_layout</tt> - If set to true and layout is not found,
-
# an ArgumentError exception is raised (defaults to false)
-
#
-
# ==== Returns
-
# * <tt>template</tt> - The template object for the default layout (or nil)
-
1
def _default_layout(require_layout = false)
-
103
begin
-
103
value = _layout if action_has_layout?
-
rescue NameError => e
-
raise e, "Could not render layout: #{e.message}"
-
end
-
-
103
if require_layout && action_has_layout? && !value
-
raise ArgumentError,
-
"There was no default layout for #{self.class} in #{view_paths.inspect}"
-
end
-
-
103
_normalize_layout(value)
-
end
-
-
1
def _include_layout?(options)
-
137
(options.keys & [:text, :inline, :partial]).empty? || options.key?(:layout)
-
end
-
end
-
end
-
1
require "active_support/benchmarkable"
-
-
1
module AbstractController
-
1
module Logger #:nodoc:
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
2
config_accessor :logger
-
2
include ActiveSupport::Benchmarkable
-
end
-
end
-
end
-
1
require "abstract_controller/base"
-
1
require "action_view"
-
-
1
module AbstractController
-
1
class DoubleRenderError < Error
-
1
DEFAULT_MESSAGE = "Render and/or redirect were called multiple times in this action. Please note that you may only call render OR redirect, and at most once per action. Also note that neither redirect nor render terminate execution of the action, so if you want to exit an action after redirecting, you need to do something like \"redirect_to(...) and return\"."
-
-
1
def initialize(message = nil)
-
super(message || DEFAULT_MESSAGE)
-
end
-
end
-
-
# This is a class to fix I18n global state. Whenever you provide I18n.locale during a request,
-
# it will trigger the lookup_context and consequently expire the cache.
-
1
class I18nProxy < ::I18n::Config #:nodoc:
-
1
attr_reader :original_config, :lookup_context
-
-
1
def initialize(original_config, lookup_context)
-
121
original_config = original_config.original_config if original_config.respond_to?(:original_config)
-
121
@original_config, @lookup_context = original_config, lookup_context
-
end
-
-
1
def locale
-
74
@original_config.locale
-
end
-
-
1
def locale=(value)
-
1
@lookup_context.locale = value
-
end
-
end
-
-
1
module Rendering
-
1
extend ActiveSupport::Concern
-
1
include AbstractController::ViewPaths
-
-
1
included do
-
2
class_attribute :protected_instance_variables
-
2
self.protected_instance_variables = []
-
end
-
-
# Overwrite process to setup I18n proxy.
-
1
def process(*) #:nodoc:
-
121
old_config, I18n.config = I18n.config, I18nProxy.new(I18n.config, lookup_context)
-
121
super
-
ensure
-
121
I18n.config = old_config
-
end
-
-
1
module ClassMethods
-
1
def view_context_class
-
@view_context_class ||= begin
-
17
routes = respond_to?(:_routes) && _routes
-
17
helpers = respond_to?(:_helpers) && _helpers
-
-
17
Class.new(ActionView::Base) do
-
17
if routes
-
16
include routes.url_helpers
-
16
include routes.mounted_helpers
-
end
-
-
17
if helpers
-
17
include helpers
-
end
-
end
-
115
end
-
end
-
end
-
-
1
attr_internal_writer :view_context_class
-
-
1
def view_context_class
-
137
@_view_context_class ||= self.class.view_context_class
-
end
-
-
# An instance of a view class. The default view class is ActionView::Base
-
#
-
# The view class must have the following methods:
-
# View.new[lookup_context, assigns, controller]
-
# Create a new ActionView instance for a controller
-
# View#render[options]
-
# Returns String with the rendered template
-
#
-
# Override this method in a module to change the default behavior.
-
1
def view_context
-
137
view_context_class.new(view_renderer, view_assigns, self)
-
end
-
-
# Returns an object that is able to render templates.
-
1
def view_renderer
-
274
@_view_renderer ||= ActionView::Renderer.new(lookup_context)
-
end
-
-
# Normalize arguments, options and then delegates render_to_body and
-
# sticks the result in self.response_body.
-
1
def render(*args, &block)
-
137
options = _normalize_render(*args, &block)
-
137
self.response_body = render_to_body(options)
-
end
-
-
# Raw rendering of a template to a string. Just convert the results of
-
# render_response into a String.
-
# :api: plugin
-
1
def render_to_string(*args, &block)
-
options = _normalize_render(*args, &block)
-
render_to_body(options)
-
end
-
-
# Raw rendering of a template to a Rack-compatible body.
-
# :api: plugin
-
1
def render_to_body(options = {})
-
137
_process_options(options)
-
137
_render_template(options)
-
end
-
-
# Find and renders a template based on the options given.
-
# :api: private
-
1
def _render_template(options) #:nodoc:
-
137
lookup_context.rendered_format = nil if options[:formats]
-
137
view_renderer.render(view_context, options)
-
end
-
-
1
DEFAULT_PROTECTED_INSTANCE_VARIABLES = [
-
:@_action_name, :@_response_body, :@_formats, :@_prefixes, :@_config,
-
:@_view_context_class, :@_view_renderer, :@_lookup_context
-
]
-
-
# This method should return a hash with assigns.
-
# You can overwrite this configuration per controller.
-
# :api: public
-
1
def view_assigns
-
137
hash = {}
-
137
variables = instance_variables
-
137
variables -= protected_instance_variables
-
137
variables -= DEFAULT_PROTECTED_INSTANCE_VARIABLES
-
432
variables.each { |name| hash[name[1..-1]] = instance_variable_get(name) }
-
137
hash
-
end
-
-
1
private
-
-
# Normalize args and options.
-
# :api: private
-
1
def _normalize_render(*args, &block)
-
137
options = _normalize_args(*args, &block)
-
137
_normalize_options(options)
-
137
options
-
end
-
-
# Normalize args by converting render "foo" to render :action => "foo" and
-
# render "foo/bar" to render :file => "foo/bar".
-
# :api: plugin
-
1
def _normalize_args(action=nil, options={})
-
137
case action
-
when NilClass
-
when Hash
-
125
options = action
-
when String, Symbol
-
8
action = action.to_s
-
8
key = action.include?(?/) ? :file : :action
-
8
options[key] = action
-
else
-
options[:partial] = action
-
end
-
-
137
options
-
end
-
-
# Normalize options.
-
# :api: plugin
-
1
def _normalize_options(options)
-
137
if options[:partial] == true
-
options[:partial] = action_name
-
end
-
-
137
if (options.keys & [:partial, :file, :template]).empty?
-
46
options[:prefixes] ||= _prefixes
-
end
-
-
137
options[:template] ||= (options[:action] || action_name).to_s
-
137
options
-
end
-
-
# Process extra options.
-
# :api: plugin
-
1
def _process_options(options)
-
end
-
end
-
end
-
1
module AbstractController
-
1
module Translation
-
1
def translate(*args)
-
key = args.first
-
if key.is_a?(String) && (key[0] == '.')
-
key = "#{ controller_path.gsub('/', '.') }.#{ action_name }#{ key }"
-
args[0] = key
-
end
-
-
I18n.translate(*args)
-
end
-
1
alias :t :translate
-
-
1
def localize(*args)
-
I18n.localize(*args)
-
end
-
1
alias :l :localize
-
end
-
end
-
1
module AbstractController
-
# Includes +url_for+ into the host class (e.g. an abstract controller or mailer). The class
-
# has to provide a +RouteSet+ by implementing the <tt>_routes</tt> methods. Otherwise, an
-
# exception will be raised.
-
#
-
# Note that this module is completely decoupled from HTTP - the only requirement is a valid
-
# <tt>_routes</tt> implementation.
-
1
module UrlFor
-
1
extend ActiveSupport::Concern
-
1
include ActionDispatch::Routing::UrlFor
-
-
1
def _routes
-
raise "In order to use #url_for, you must include routing helpers explicitly. " \
-
"For instance, `include Rails.application.routes.url_helpers"
-
end
-
-
1
module ClassMethods
-
1
def _routes
-
nil
-
end
-
-
1
def action_methods
-
@action_methods ||= begin
-
1
if _routes
-
super - _routes.named_routes.helper_names
-
else
-
1
super
-
end
-
2
end
-
end
-
end
-
end
-
end
-
1
require 'action_view/base'
-
-
1
module AbstractController
-
1
module ViewPaths
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
2
class_attribute :_view_paths
-
2
self._view_paths = ActionView::PathSet.new
-
2
self._view_paths.freeze
-
end
-
-
1
delegate :template_exists?, :view_paths, :formats, :formats=,
-
:locale, :locale=, :to => :lookup_context
-
-
1
module ClassMethods
-
1
def parent_prefixes
-
@parent_prefixes ||= begin
-
18
parent_controller = superclass
-
18
prefixes = []
-
-
18
until parent_controller.abstract?
-
1
prefixes << parent_controller.controller_path
-
1
parent_controller = parent_controller.superclass
-
end
-
-
18
prefixes
-
121
end
-
end
-
end
-
-
# The prefixes used in render "foo" shortcuts.
-
1
def _prefixes
-
@_prefixes ||= begin
-
121
parent_prefixes = self.class.parent_prefixes
-
121
parent_prefixes.dup.unshift(controller_path)
-
167
end
-
end
-
-
# LookupContext is the object responsible to hold all information required to lookup
-
# templates, i.e. view paths and details. Check ActionView::LookupContext for more
-
# information.
-
1
def lookup_context
-
@_lookup_context ||=
-
747
ActionView::LookupContext.new(self.class._view_paths, details_for_lookup, _prefixes)
-
end
-
-
1
def details_for_lookup
-
121
{ }
-
end
-
-
1
def append_view_path(path)
-
lookup_context.view_paths.push(*path)
-
end
-
-
1
def prepend_view_path(path)
-
lookup_context.view_paths.unshift(*path)
-
end
-
-
1
module ClassMethods
-
# Append a path to the list of view paths for this controller.
-
#
-
# ==== Parameters
-
# * <tt>path</tt> - If a String is provided, it gets converted into
-
# the default view path. You may also provide a custom view path
-
# (see ActionView::PathSet for more information)
-
1
def append_view_path(path)
-
self._view_paths = view_paths + Array(path)
-
end
-
-
# Prepend a path to the list of view paths for this controller.
-
#
-
# ==== Parameters
-
# * <tt>path</tt> - If a String is provided, it gets converted into
-
# the default view path. You may also provide a custom view path
-
# (see ActionView::PathSet for more information)
-
1
def prepend_view_path(path)
-
self._view_paths = ActionView::PathSet.new(Array(path) + view_paths)
-
end
-
-
# A list of all of the default view paths for this controller.
-
1
def view_paths
-
2
_view_paths
-
end
-
-
# Set the view paths.
-
#
-
# ==== Parameters
-
# * <tt>paths</tt> - If a PathSet is provided, use that;
-
# otherwise, process the parameter into a PathSet.
-
1
def view_paths=(paths)
-
5
self._view_paths = ActionView::PathSet.new(Array(paths))
-
end
-
end
-
end
-
end
-
1
require 'active_support/rails'
-
1
require 'abstract_controller'
-
1
require 'action_dispatch'
-
1
require 'action_controller/metal/live'
-
1
require 'action_controller/metal/strong_parameters'
-
-
1
module ActionController
-
1
extend ActiveSupport::Autoload
-
-
1
autoload :Base
-
1
autoload :Caching
-
1
autoload :Metal
-
1
autoload :Middleware
-
-
1
autoload_under "metal" do
-
1
autoload :Compatibility
-
1
autoload :ConditionalGet
-
1
autoload :Cookies
-
1
autoload :DataStreaming
-
1
autoload :Flash
-
1
autoload :ForceSSL
-
1
autoload :Head
-
1
autoload :Helpers
-
1
autoload :HideActions
-
1
autoload :HttpAuthentication
-
1
autoload :ImplicitRender
-
1
autoload :Instrumentation
-
1
autoload :MimeResponds
-
1
autoload :ParamsWrapper
-
1
autoload :RackDelegation
-
1
autoload :Redirecting
-
1
autoload :Renderers
-
1
autoload :Rendering
-
1
autoload :RequestForgeryProtection
-
1
autoload :Rescue
-
1
autoload :Responder
-
1
autoload :Streaming
-
1
autoload :StrongParameters
-
1
autoload :Testing
-
1
autoload :UrlFor
-
end
-
-
1
autoload :Integration, 'action_controller/deprecated/integration_test'
-
1
autoload :IntegrationTest, 'action_controller/deprecated/integration_test'
-
1
autoload :PerformanceTest, 'action_controller/deprecated/performance_test'
-
1
autoload :Routing, 'action_controller/deprecated'
-
1
autoload :TestCase, 'action_controller/test_case'
-
1
autoload :TemplateAssertions, 'action_controller/test_case'
-
-
1
eager_autoload do
-
1
autoload :RecordIdentifier
-
end
-
-
1
def self.eager_load!
-
super
-
ActionController::Caching.eager_load!
-
HTML.eager_load!
-
end
-
end
-
-
# All of these simply register additional autoloads
-
1
require 'action_view'
-
1
require 'action_view/vendor/html-scanner'
-
-
1
ActiveSupport.on_load(:action_view) do
-
1
ActionView::RoutingUrlFor.send(:include, ActionDispatch::Routing::UrlFor)
-
end
-
-
# Common Active Support usage in Action Controller
-
1
require 'active_support/core_ext/class/attribute_accessors'
-
1
require 'active_support/core_ext/load_error'
-
1
require 'active_support/core_ext/module/attr_internal'
-
1
require 'active_support/core_ext/name_error'
-
1
require 'active_support/core_ext/uri'
-
1
require 'active_support/inflector'
-
1
require "action_controller/log_subscriber"
-
-
1
module ActionController
-
# Action Controllers are the core of a web request in \Rails. They are made up of one or more actions that are executed
-
# on request and then either render a template or redirect to another action. An action is defined as a public method
-
# on the controller, which will automatically be made accessible to the web-server through \Rails Routes.
-
#
-
# By default, only the ApplicationController in a \Rails application inherits from <tt>ActionController::Base</tt>. All other
-
# controllers in turn inherit from ApplicationController. This gives you one class to configure things such as
-
# request forgery protection and filtering of sensitive request parameters.
-
#
-
# A sample controller could look like this:
-
#
-
# class PostsController < ApplicationController
-
# def index
-
# @posts = Post.all
-
# end
-
#
-
# def create
-
# @post = Post.create params[:post]
-
# redirect_to posts_path
-
# end
-
# end
-
#
-
# Actions, by default, render a template in the <tt>app/views</tt> directory corresponding to the name of the controller and action
-
# after executing code in the action. For example, the +index+ action of the PostsController would render the
-
# template <tt>app/views/posts/index.html.erb</tt> by default after populating the <tt>@posts</tt> instance variable.
-
#
-
# Unlike index, the create action will not render a template. After performing its main purpose (creating a
-
# new post), it initiates a redirect instead. This redirect works by returning an external
-
# "302 Moved" HTTP response that takes the user to the index action.
-
#
-
# These two methods represent the two basic action archetypes used in Action Controllers. Get-and-show and do-and-redirect.
-
# Most actions are variations on these themes.
-
#
-
# == Requests
-
#
-
# For every request, the router determines the value of the +controller+ and +action+ keys. These determine which controller
-
# and action are called. The remaining request parameters, the session (if one is available), and the full request with
-
# all the HTTP headers are made available to the action through accessor methods. Then the action is performed.
-
#
-
# The full request object is available via the request accessor and is primarily used to query for HTTP headers:
-
#
-
# def server_ip
-
# location = request.env["SERVER_ADDR"]
-
# render text: "This server hosted at #{location}"
-
# end
-
#
-
# == Parameters
-
#
-
# All request parameters, whether they come from a GET or POST request, or from the URL, are available through the params method
-
# which returns a hash. For example, an action that was performed through <tt>/posts?category=All&limit=5</tt> will include
-
# <tt>{ "category" => "All", "limit" => "5" }</tt> in params.
-
#
-
# It's also possible to construct multi-dimensional parameter hashes by specifying keys using brackets, such as:
-
#
-
# <input type="text" name="post[name]" value="david">
-
# <input type="text" name="post[address]" value="hyacintvej">
-
#
-
# A request stemming from a form holding these inputs will include <tt>{ "post" => { "name" => "david", "address" => "hyacintvej" } }</tt>.
-
# If the address input had been named "post[address][street]", the params would have included
-
# <tt>{ "post" => { "address" => { "street" => "hyacintvej" } } }</tt>. There's no limit to the depth of the nesting.
-
#
-
# == Sessions
-
#
-
# Sessions allow you to store objects in between requests. This is useful for objects that are not yet ready to be persisted,
-
# such as a Signup object constructed in a multi-paged process, or objects that don't change much and are needed all the time, such
-
# as a User object for a system that requires login. The session should not be used, however, as a cache for objects where it's likely
-
# they could be changed unknowingly. It's usually too much work to keep it all synchronized -- something databases already excel at.
-
#
-
# You can place objects in the session by using the <tt>session</tt> method, which accesses a hash:
-
#
-
# session[:person] = Person.authenticate(user_name, password)
-
#
-
# And retrieved again through the same hash:
-
#
-
# Hello #{session[:person]}
-
#
-
# For removing objects from the session, you can either assign a single key to +nil+:
-
#
-
# # removes :person from session
-
# session[:person] = nil
-
#
-
# or you can remove the entire session with +reset_session+.
-
#
-
# Sessions are stored by default in a browser cookie that's cryptographically signed, but unencrypted.
-
# This prevents the user from tampering with the session but also allows him to see its contents.
-
#
-
# Do not put secret information in cookie-based sessions!
-
#
-
# == Responses
-
#
-
# Each action results in a response, which holds the headers and document to be sent to the user's browser. The actual response
-
# object is generated automatically through the use of renders and redirects and requires no user intervention.
-
#
-
# == Renders
-
#
-
# Action Controller sends content to the user by using one of five rendering methods. The most versatile and common is the rendering
-
# of a template. Included in the Action Pack is the Action View, which enables rendering of ERB templates. It's automatically configured.
-
# The controller passes objects to the view by assigning instance variables:
-
#
-
# def show
-
# @post = Post.find(params[:id])
-
# end
-
#
-
# Which are then automatically available to the view:
-
#
-
# Title: <%= @post.title %>
-
#
-
# You don't have to rely on the automated rendering. For example, actions that could result in the rendering of different templates
-
# will use the manual rendering methods:
-
#
-
# def search
-
# @results = Search.find(params[:query])
-
# case @results.count
-
# when 0 then render action: "no_results"
-
# when 1 then render action: "show"
-
# when 2..10 then render action: "show_many"
-
# end
-
# end
-
#
-
# Read more about writing ERB and Builder templates in ActionView::Base.
-
#
-
# == Redirects
-
#
-
# Redirects are used to move from one action to another. For example, after a <tt>create</tt> action, which stores a blog entry to the
-
# database, we might like to show the user the new entry. Because we're following good DRY principles (Don't Repeat Yourself), we're
-
# going to reuse (and redirect to) a <tt>show</tt> action that we'll assume has already been created. The code might look like this:
-
#
-
# def create
-
# @entry = Entry.new(params[:entry])
-
# if @entry.save
-
# # The entry was saved correctly, redirect to show
-
# redirect_to action: 'show', id: @entry.id
-
# else
-
# # things didn't go so well, do something else
-
# end
-
# end
-
#
-
# In this case, after saving our new entry to the database, the user is redirected to the <tt>show</tt> method, which is then executed.
-
# Note that this is an external HTTP-level redirection which will cause the browser to make a second request (a GET to the show action),
-
# and not some internal re-routing which calls both "create" and then "show" within one request.
-
#
-
# Learn more about <tt>redirect_to</tt> and what options you have in ActionController::Redirecting.
-
#
-
# == Calling multiple redirects or renders
-
#
-
# An action may contain only a single render or a single redirect. Attempting to try to do either again will result in a DoubleRenderError:
-
#
-
# def do_something
-
# redirect_to action: "elsewhere"
-
# render action: "overthere" # raises DoubleRenderError
-
# end
-
#
-
# If you need to redirect on the condition of something, then be sure to add "and return" to halt execution.
-
#
-
# def do_something
-
# redirect_to(action: "elsewhere") and return if monkeys.nil?
-
# render action: "overthere" # won't be called if monkeys is nil
-
# end
-
#
-
1
class Base < Metal
-
1
abstract!
-
-
# We document the request and response methods here because albeit they are
-
# implemented in ActionController::Metal, the type of the returned objects
-
# is unknown at that level.
-
-
##
-
# :method: request
-
#
-
# Returns an ActionDispatch::Request instance that represents the
-
# current request.
-
-
##
-
# :method: response
-
#
-
# Returns an ActionDispatch::Response that represents the current
-
# response.
-
-
# Shortcut helper that returns all the modules included in
-
# ActionController::Base except the ones passed as arguments:
-
#
-
# class MetalController
-
# ActionController::Base.without_modules(:ParamsWrapper, :Streaming).each do |left|
-
# include left
-
# end
-
# end
-
#
-
# This gives better control over what you want to exclude and makes it
-
# easier to create a bare controller class, instead of listing the modules
-
# required manually.
-
1
def self.without_modules(*modules)
-
modules = modules.map do |m|
-
m.is_a?(Symbol) ? ActionController.const_get(m) : m
-
end
-
-
MODULES - modules
-
end
-
-
1
MODULES = [
-
AbstractController::Layouts,
-
AbstractController::Translation,
-
AbstractController::AssetPaths,
-
-
Helpers,
-
HideActions,
-
UrlFor,
-
Redirecting,
-
Rendering,
-
Renderers::All,
-
ConditionalGet,
-
RackDelegation,
-
Caching,
-
MimeResponds,
-
ImplicitRender,
-
StrongParameters,
-
-
Cookies,
-
Flash,
-
RequestForgeryProtection,
-
ForceSSL,
-
Streaming,
-
DataStreaming,
-
RecordIdentifier,
-
HttpAuthentication::Basic::ControllerMethods,
-
HttpAuthentication::Digest::ControllerMethods,
-
HttpAuthentication::Token::ControllerMethods,
-
-
# Before callbacks should also be executed the earliest as possible, so
-
# also include them at the bottom.
-
AbstractController::Callbacks,
-
-
# Append rescue at the bottom to wrap as much as possible.
-
Rescue,
-
-
# Add instrumentations hooks at the bottom, to ensure they instrument
-
# all the methods properly.
-
Instrumentation,
-
-
# Params wrapper should come before instrumentation so they are
-
# properly showed in logs
-
ParamsWrapper
-
]
-
-
1
MODULES.each do |mod|
-
29
include mod
-
end
-
-
# Define some internal variables that should not be propagated to the view.
-
1
self.protected_instance_variables = [
-
:@_status, :@_headers, :@_params, :@_env, :@_response, :@_request,
-
:@_view_runtime, :@_stream, :@_url_options, :@_action_has_layout
-
]
-
-
1
ActiveSupport.run_load_hooks(:action_controller, self)
-
end
-
end
-
1
require 'fileutils'
-
1
require 'uri'
-
1
require 'set'
-
-
1
module ActionController
-
# \Caching is a cheap way of speeding up slow applications by keeping the result of
-
# calculations, renderings, and database calls around for subsequent requests.
-
#
-
# You can read more about each approach and the sweeping assistance by clicking the
-
# modules below.
-
#
-
# Note: To turn off all caching and sweeping, set
-
# config.action_controller.perform_caching = false.
-
#
-
# == \Caching stores
-
#
-
# All the caching stores from ActiveSupport::Cache are available to be used as backends
-
# for Action Controller caching.
-
#
-
# Configuration examples (MemoryStore is the default):
-
#
-
# config.action_controller.cache_store = :memory_store
-
# config.action_controller.cache_store = :file_store, '/path/to/cache/directory'
-
# config.action_controller.cache_store = :mem_cache_store, 'localhost'
-
# config.action_controller.cache_store = :mem_cache_store, Memcached::Rails.new('localhost:11211')
-
# config.action_controller.cache_store = MyOwnStore.new('parameter')
-
1
module Caching
-
1
extend ActiveSupport::Concern
-
1
extend ActiveSupport::Autoload
-
-
1
eager_autoload do
-
1
autoload :Fragments
-
1
autoload :Sweeper, 'action_controller/caching/sweeping'
-
1
autoload :Sweeping, 'action_controller/caching/sweeping'
-
end
-
-
1
module ConfigMethods
-
1
def cache_store
-
config.cache_store
-
end
-
-
1
def cache_store=(store)
-
config.cache_store = ActiveSupport::Cache.lookup_store(store)
-
end
-
-
1
private
-
1
def cache_configured?
-
perform_caching && cache_store
-
end
-
end
-
-
1
include RackDelegation
-
1
include AbstractController::Callbacks
-
-
1
include ConfigMethods
-
1
include Fragments
-
1
include Sweeping if defined?(ActiveRecord)
-
-
1
included do
-
1
extend ConfigMethods
-
-
1
config_accessor :default_static_extension
-
1
self.default_static_extension ||= '.html'
-
-
1
def self.page_cache_extension=(extension)
-
ActiveSupport::Deprecation.deprecation_warning(:page_cache_extension, :default_static_extension)
-
self.default_static_extension = extension
-
end
-
-
1
def self.page_cache_extension
-
ActiveSupport::Deprecation.deprecation_warning(:page_cache_extension, :default_static_extension)
-
default_static_extension
-
end
-
-
1
config_accessor :perform_caching
-
1
self.perform_caching = true if perform_caching.nil?
-
end
-
-
1
def caching_allowed?
-
request.get? && response.status == 200
-
end
-
-
1
protected
-
# Convenience accessor.
-
1
def cache(key, options = {}, &block)
-
if cache_configured?
-
cache_store.fetch(ActiveSupport::Cache.expand_cache_key(key, :controller), options, &block)
-
else
-
yield
-
end
-
end
-
end
-
end
-
1
module ActionController
-
1
module Caching
-
# Fragment caching is used for caching various blocks within
-
# views without caching the entire action as a whole. This is
-
# useful when certain elements of an action change frequently or
-
# depend on complicated state while other parts rarely change or
-
# can be shared amongst multiple parties. The caching is done using
-
# the +cache+ helper available in the Action View. See
-
# ActionView::Helpers::CacheHelper for more information.
-
#
-
# While it's strongly recommended that you use key-based cache
-
# expiration (see links in CacheHelper for more information),
-
# it is also possible to manually expire caches. For example:
-
#
-
# expire_fragment('name_of_cache')
-
1
module Fragments
-
# Given a key (as described in +expire_fragment+), returns
-
# a key suitable for use in reading, writing, or expiring a
-
# cached fragment. All keys are prefixed with <tt>views/</tt> and uses
-
# ActiveSupport::Cache.expand_cache_key for the expansion.
-
1
def fragment_cache_key(key)
-
ActiveSupport::Cache.expand_cache_key(key.is_a?(Hash) ? url_for(key).split("://").last : key, :views)
-
end
-
-
# Writes +content+ to the location signified by
-
# +key+ (see +expire_fragment+ for acceptable formats).
-
1
def write_fragment(key, content, options = nil)
-
return content unless cache_configured?
-
-
key = fragment_cache_key(key)
-
instrument_fragment_cache :write_fragment, key do
-
content = content.to_str
-
cache_store.write(key, content, options)
-
end
-
content
-
end
-
-
# Reads a cached fragment from the location signified by +key+
-
# (see +expire_fragment+ for acceptable formats).
-
1
def read_fragment(key, options = nil)
-
return unless cache_configured?
-
-
key = fragment_cache_key(key)
-
instrument_fragment_cache :read_fragment, key do
-
result = cache_store.read(key, options)
-
result.respond_to?(:html_safe) ? result.html_safe : result
-
end
-
end
-
-
# Check if a cached fragment from the location signified by
-
# +key+ exists (see +expire_fragment+ for acceptable formats).
-
1
def fragment_exist?(key, options = nil)
-
return unless cache_configured?
-
key = fragment_cache_key(key)
-
-
instrument_fragment_cache :exist_fragment?, key do
-
cache_store.exist?(key, options)
-
end
-
end
-
-
# Removes fragments from the cache.
-
#
-
# +key+ can take one of three forms:
-
#
-
# * String - This would normally take the form of a path, like
-
# <tt>pages/45/notes</tt>.
-
# * Hash - Treated as an implicit call to +url_for+, like
-
# <tt>{ controller: 'pages', action: 'notes', id: 45}</tt>
-
# * Regexp - Will remove any fragment that matches, so
-
# <tt>%r{pages/\d*/notes}</tt> might remove all notes. Make sure you
-
# don't use anchors in the regex (<tt>^</tt> or <tt>$</tt>) because
-
# the actual filename matched looks like
-
# <tt>./cache/filename/path.cache</tt>. Note: Regexp expiration is
-
# only supported on caches that can iterate over all keys (unlike
-
# memcached).
-
#
-
# +options+ is passed through to the cache store's +delete+
-
# method (or <tt>delete_matched</tt>, for Regexp keys).
-
1
def expire_fragment(key, options = nil)
-
return unless cache_configured?
-
key = fragment_cache_key(key) unless key.is_a?(Regexp)
-
-
instrument_fragment_cache :expire_fragment, key do
-
if key.is_a?(Regexp)
-
cache_store.delete_matched(key, options)
-
else
-
cache_store.delete(key, options)
-
end
-
end
-
end
-
-
1
def instrument_fragment_cache(name, key) # :nodoc:
-
ActiveSupport::Notifications.instrument("#{name}.action_controller", :key => key){ yield }
-
end
-
end
-
end
-
end
-
1
module ActionController
-
1
module Caching
-
# Sweepers are the terminators of the caching world and responsible for expiring
-
# caches when Active Record objects change. They do this by being half-observers,
-
# half-filters and implementing callbacks for both roles.
-
#
-
# class ListSweeper < ActionController::Caching::Sweeper
-
# observe List, Item
-
#
-
# def after_save(record)
-
# list = record.is_a?(List) ? record : record.list
-
# expire_page(controller: 'lists', action: %w( show public feed ), id: list.id)
-
# expire_action(controller: 'lists', action: 'all')
-
# list.shares.each { |share| expire_page(controller: 'lists', action: 'show', id: share.url_key) }
-
# end
-
# end
-
#
-
# The sweeper is assigned in the controllers that wish to have its job performed using
-
# the +cache_sweeper+ class method:
-
#
-
# class ListsController < ApplicationController
-
# caches_action :index, :show, :public, :feed
-
# cache_sweeper :list_sweeper, only: [ :edit, :destroy, :share ]
-
# end
-
#
-
# In the example above, four actions are cached and three actions are responsible for expiring those caches.
-
#
-
# You can also name an explicit class in the declaration of a sweeper, which is needed
-
# if the sweeper is in a module:
-
#
-
# class ListsController < ApplicationController
-
# caches_action :index, :show, :public, :feed
-
# cache_sweeper OpenBar::Sweeper, only: [ :edit, :destroy, :share ]
-
# end
-
1
module Sweeping
-
1
extend ActiveSupport::Concern
-
-
1
module ClassMethods # :nodoc:
-
1
def cache_sweeper(*sweepers)
-
configuration = sweepers.extract_options!
-
-
sweepers.each do |sweeper|
-
ActiveRecord::Base.observers << sweeper if defined?(ActiveRecord) and defined?(ActiveRecord::Base)
-
sweeper_instance = (sweeper.is_a?(Symbol) ? Object.const_get(sweeper.to_s.classify) : sweeper).instance
-
-
if sweeper_instance.is_a?(Sweeper)
-
around_filter(sweeper_instance, :only => configuration[:only])
-
else
-
after_filter(sweeper_instance, :only => configuration[:only])
-
end
-
end
-
end
-
end
-
end
-
-
1
if defined?(ActiveRecord) and defined?(ActiveRecord::Observer)
-
class Sweeper < ActiveRecord::Observer # :nodoc:
-
attr_accessor :controller
-
-
def initialize(*args)
-
super
-
@controller = nil
-
end
-
-
def before(controller)
-
self.controller = controller
-
callback(:before) if controller.perform_caching
-
true # before method from sweeper should always return true
-
end
-
-
def after(controller)
-
self.controller = controller
-
callback(:after) if controller.perform_caching
-
end
-
-
def around(controller)
-
before(controller)
-
yield
-
after(controller)
-
ensure
-
clean_up
-
end
-
-
protected
-
# gets the action cache path for the given options.
-
def action_path_for(options)
-
Actions::ActionCachePath.new(controller, options).path
-
end
-
-
# Retrieve instance variables set in the controller.
-
def assigns(key)
-
controller.instance_variable_get("@#{key}")
-
end
-
-
private
-
def clean_up
-
# Clean up, so that the controller can be collected after this request
-
self.controller = nil
-
end
-
-
def callback(timing)
-
controller_callback_method_name = "#{timing}_#{controller.controller_name.underscore}"
-
action_callback_method_name = "#{controller_callback_method_name}_#{controller.action_name}"
-
-
__send__(controller_callback_method_name) if respond_to?(controller_callback_method_name, true)
-
__send__(action_callback_method_name) if respond_to?(action_callback_method_name, true)
-
end
-
-
def method_missing(method, *arguments, &block)
-
return super unless @controller
-
@controller.__send__(method, *arguments, &block)
-
end
-
end
-
end
-
end
-
end
-
-
1
module ActionController
-
1
class LogSubscriber < ActiveSupport::LogSubscriber
-
1
INTERNAL_PARAMS = %w(controller action format _method only_path)
-
-
1
def start_processing(event)
-
return unless logger.info?
-
-
payload = event.payload
-
params = payload[:params].except(*INTERNAL_PARAMS)
-
format = payload[:format]
-
format = format.to_s.upcase if format.is_a?(Symbol)
-
-
info "Processing by #{payload[:controller]}##{payload[:action]} as #{format}"
-
info " Parameters: #{params.inspect}" unless params.empty?
-
end
-
-
1
def process_action(event)
-
return unless logger.info?
-
-
payload = event.payload
-
additions = ActionController::Base.log_process_action(payload)
-
-
status = payload[:status]
-
if status.nil? && payload[:exception].present?
-
exception_class_name = payload[:exception].first
-
status = ActionDispatch::ExceptionWrapper.status_code_for_exception(exception_class_name)
-
end
-
message = "Completed #{status} #{Rack::Utils::HTTP_STATUS_CODES[status]} in #{event.duration.round}ms"
-
message << " (#{additions.join(" | ")})" unless additions.blank?
-
-
info(message)
-
end
-
-
1
def halted_callback(event)
-
info("Filter chain halted as #{event.payload[:filter]} rendered or redirected")
-
end
-
-
1
def send_file(event)
-
info("Sent file #{event.payload[:path]} (#{event.duration.round(1)}ms)")
-
end
-
-
1
def redirect_to(event)
-
info("Redirected to #{event.payload[:location]}")
-
end
-
-
1
def send_data(event)
-
info("Sent data #{event.payload[:filename]} (#{event.duration.round(1)}ms)")
-
end
-
-
1
%w(write_fragment read_fragment exist_fragment?
-
expire_fragment expire_page write_page).each do |method|
-
6
class_eval <<-METHOD, __FILE__, __LINE__ + 1
-
def #{method}(event)
-
return unless logger.info?
-
key_or_path = event.payload[:key] || event.payload[:path]
-
human_name = #{method.to_s.humanize.inspect}
-
info("\#{human_name} \#{key_or_path} (\#{event.duration.round(1)}ms)")
-
end
-
METHOD
-
end
-
-
1
def logger
-
4
ActionController::Base.logger
-
end
-
end
-
end
-
-
1
ActionController::LogSubscriber.attach_to :action_controller
-
1
require 'action_dispatch/middleware/stack'
-
-
1
module ActionController
-
# Extend ActionDispatch middleware stack to make it aware of options
-
# allowing the following syntax in controllers:
-
#
-
# class PostsController < ApplicationController
-
# use AuthenticationMiddleware, except: [:index, :show]
-
# end
-
#
-
1
class MiddlewareStack < ActionDispatch::MiddlewareStack #:nodoc:
-
1
class Middleware < ActionDispatch::MiddlewareStack::Middleware #:nodoc:
-
1
def initialize(klass, *args, &block)
-
options = args.extract_options!
-
@only = Array(options.delete(:only)).map(&:to_s)
-
@except = Array(options.delete(:except)).map(&:to_s)
-
args << options unless options.empty?
-
super
-
end
-
-
1
def valid?(action)
-
if @only.present?
-
@only.include?(action)
-
elsif @except.present?
-
!@except.include?(action)
-
else
-
true
-
end
-
end
-
end
-
-
1
def build(action, app=nil, &block)
-
1
app ||= block
-
1
action = action.to_s
-
1
raise "MiddlewareStack#build requires an app" unless app
-
-
1
middlewares.reverse.inject(app) do |a, middleware|
-
middleware.valid?(action) ?
-
middleware.build(a) : a
-
end
-
end
-
end
-
-
# <tt>ActionController::Metal</tt> is the simplest possible controller, providing a
-
# valid Rack interface without the additional niceties provided by
-
# <tt>ActionController::Base</tt>.
-
#
-
# A sample metal controller might look like this:
-
#
-
# class HelloController < ActionController::Metal
-
# def index
-
# self.response_body = "Hello World!"
-
# end
-
# end
-
#
-
# And then to route requests to your metal controller, you would add
-
# something like this to <tt>config/routes.rb</tt>:
-
#
-
# match 'hello', to: HelloController.action(:index)
-
#
-
# The +action+ method returns a valid Rack application for the \Rails
-
# router to dispatch to.
-
#
-
# == Rendering Helpers
-
#
-
# <tt>ActionController::Metal</tt> by default provides no utilities for rendering
-
# views, partials, or other responses aside from explicitly calling of
-
# <tt>response_body=</tt>, <tt>content_type=</tt>, and <tt>status=</tt>. To
-
# add the render helpers you're used to having in a normal controller, you
-
# can do the following:
-
#
-
# class HelloController < ActionController::Metal
-
# include ActionController::Rendering
-
# append_view_path "#{Rails.root}/app/views"
-
#
-
# def index
-
# render "hello/index"
-
# end
-
# end
-
#
-
# == Redirection Helpers
-
#
-
# To add redirection helpers to your metal controller, do the following:
-
#
-
# class HelloController < ActionController::Metal
-
# include ActionController::Redirecting
-
# include Rails.application.routes.url_helpers
-
#
-
# def index
-
# redirect_to root_url
-
# end
-
# end
-
#
-
# == Other Helpers
-
#
-
# You can refer to the modules included in <tt>ActionController::Base</tt> to see
-
# other features you can bring into your metal controller.
-
#
-
1
class Metal < AbstractController::Base
-
1
abstract!
-
-
1
attr_internal_writer :env
-
-
1
def env
-
@_env ||= {}
-
end
-
-
# Returns the last part of the controller's name, underscored, without the ending
-
# <tt>Controller</tt>. For instance, PostsController returns <tt>posts</tt>.
-
# Namespaces are left out, so Admin::PostsController returns <tt>posts</tt> as well.
-
#
-
# ==== Returns
-
# * <tt>string</tt>
-
1
def self.controller_name
-
@controller_name ||= name.demodulize.sub(/Controller$/, '').underscore
-
end
-
-
# Delegates to the class' <tt>controller_name</tt>
-
1
def controller_name
-
self.class.controller_name
-
end
-
-
# The details below can be overridden to support a specific
-
# Request and Response object. The default ActionController::Base
-
# implementation includes RackDelegation, which makes a request
-
# and response object available. You might wish to control the
-
# environment and response manually for performance reasons.
-
-
1
attr_internal :headers, :response, :request
-
1
delegate :session, :to => "@_request"
-
-
1
def initialize
-
1
@_headers = {"Content-Type" => "text/html"}
-
1
@_status = 200
-
1
@_request = nil
-
1
@_response = nil
-
1
@_routes = nil
-
1
super
-
end
-
-
1
def params
-
@_params ||= request.parameters
-
end
-
-
1
def params=(val)
-
@_params = val
-
end
-
-
# Basic implementations for content_type=, location=, and headers are
-
# provided to reduce the dependency on the RackDelegation module
-
# in Renderer and Redirector.
-
-
1
def content_type=(type)
-
headers["Content-Type"] = type.to_s
-
end
-
-
1
def content_type
-
headers["Content-Type"]
-
end
-
-
1
def location
-
headers["Location"]
-
end
-
-
1
def location=(url)
-
headers["Location"] = url
-
end
-
-
# basic url_for that can be overridden for more robust functionality
-
1
def url_for(string)
-
string
-
end
-
-
1
def status
-
@_status
-
end
-
-
1
def status=(status)
-
@_status = Rack::Utils.status_code(status)
-
end
-
-
1
def response_body=(body)
-
1
body = [body] unless body.nil? || body.respond_to?(:each)
-
1
super
-
end
-
-
1
def performed?
-
1
response_body || (response && response.committed?)
-
end
-
-
1
def dispatch(name, request) #:nodoc:
-
1
@_request = request
-
1
@_env = request.env
-
1
@_env['action_controller.instance'] = self
-
1
process(name)
-
1
to_a
-
end
-
-
1
def to_a #:nodoc:
-
1
response ? response.to_a : [status, headers, response_body]
-
end
-
-
1
class_attribute :middleware_stack
-
1
self.middleware_stack = ActionController::MiddlewareStack.new
-
-
1
def self.inherited(base) # :nodoc:
-
3
base.middleware_stack = middleware_stack.dup
-
3
super
-
end
-
-
# Pushes the given Rack middleware and its arguments to the bottom of the
-
# middleware stack.
-
1
def self.use(*args, &block)
-
middleware_stack.use(*args, &block)
-
end
-
-
# Alias for +middleware_stack+.
-
1
def self.middleware
-
middleware_stack
-
end
-
-
# Makes the controller a Rack endpoint that runs the action in the given
-
# +env+'s +action_dispatch.request.path_parameters+ key.
-
1
def self.call(env)
-
action(env['action_dispatch.request.path_parameters'][:action]).call(env)
-
end
-
-
# Returns a Rack endpoint for the given action name.
-
1
def self.action(name, klass = ActionDispatch::Request)
-
1
middleware_stack.build(name.to_s) do |env|
-
1
new.dispatch(name, klass.new(env))
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/class/attribute'
-
-
1
module ActionController
-
1
module ConditionalGet
-
1
extend ActiveSupport::Concern
-
-
1
include RackDelegation
-
1
include Head
-
-
1
included do
-
1
class_attribute :etaggers
-
1
self.etaggers = []
-
end
-
-
1
module ClassMethods
-
# Allows you to consider additional controller-wide information when generating an etag.
-
# For example, if you serve pages tailored depending on who's logged in at the moment, you
-
# may want to add the current user id to be part of the etag to prevent authorized displaying
-
# of cached pages.
-
#
-
# class InvoicesController < ApplicationController
-
# etag { current_user.try :id }
-
#
-
# def show
-
# # Etag will differ even for the same invoice when it's viewed by a different current_user
-
# @invoice = Invoice.find(params[:id])
-
# fresh_when(@invoice)
-
# end
-
# end
-
1
def etag(&etagger)
-
self.etaggers += [etagger]
-
end
-
end
-
-
# Sets the etag, +last_modified+, or both on the response and renders a
-
# <tt>304 Not Modified</tt> response if the request is already fresh.
-
#
-
# === Parameters:
-
#
-
# * <tt>:etag</tt>.
-
# * <tt>:last_modified</tt>.
-
# * <tt>:public</tt> By default the Cache-Control header is private, set this to
-
# +true+ if you want your application to be cachable by other devices (proxy caches).
-
#
-
# === Example:
-
#
-
# def show
-
# @article = Article.find(params[:id])
-
# fresh_when(etag: @article, last_modified: @article.created_at, public: true)
-
# end
-
#
-
# This will render the show template if the request isn't sending a matching etag or
-
# If-Modified-Since header and just a <tt>304 Not Modified</tt> response if there's a match.
-
#
-
# You can also just pass a record where +last_modified+ will be set by calling
-
# +updated_at+ and the etag by passing the object itself.
-
#
-
# def show
-
# @article = Article.find(params[:id])
-
# fresh_when(@article)
-
# end
-
#
-
# When passing a record, you can still set whether the public header:
-
#
-
# def show
-
# @article = Article.find(params[:id])
-
# fresh_when(@article, public: true)
-
# end
-
1
def fresh_when(record_or_options, additional_options = {})
-
if record_or_options.is_a? Hash
-
options = record_or_options
-
options.assert_valid_keys(:etag, :last_modified, :public)
-
else
-
record = record_or_options
-
options = { etag: record, last_modified: record.try(:updated_at) }.merge!(additional_options)
-
end
-
-
response.etag = combine_etags(options[:etag]) if options[:etag]
-
response.last_modified = options[:last_modified] if options[:last_modified]
-
response.cache_control[:public] = true if options[:public]
-
-
head :not_modified if request.fresh?(response)
-
end
-
-
# Sets the +etag+ and/or +last_modified+ on the response and checks it against
-
# the client request. If the request doesn't match the options provided, the
-
# request is considered stale and should be generated from scratch. Otherwise,
-
# it's fresh and we don't need to generate anything and a reply of <tt>304 Not Modified</tt> is sent.
-
#
-
# === Parameters:
-
#
-
# * <tt>:etag</tt>.
-
# * <tt>:last_modified</tt>.
-
# * <tt>:public</tt> By default the Cache-Control header is private, set this to
-
# +true+ if you want your application to be cachable by other devices (proxy caches).
-
#
-
# === Example:
-
#
-
# def show
-
# @article = Article.find(params[:id])
-
#
-
# if stale?(etag: @article, last_modified: @article.created_at)
-
# @statistics = @article.really_expensive_call
-
# respond_to do |format|
-
# # all the supported formats
-
# end
-
# end
-
# end
-
#
-
# You can also just pass a record where +last_modified+ will be set by calling
-
# updated_at and the etag by passing the object itself.
-
#
-
# def show
-
# @article = Article.find(params[:id])
-
#
-
# if stale?(@article)
-
# @statistics = @article.really_expensive_call
-
# respond_to do |format|
-
# # all the supported formats
-
# end
-
# end
-
# end
-
#
-
# When passing a record, you can still set whether the public header:
-
#
-
# def show
-
# @article = Article.find(params[:id])
-
#
-
# if stale?(@article, public: true)
-
# @statistics = @article.really_expensive_call
-
# respond_to do |format|
-
# # all the supported formats
-
# end
-
# end
-
# end
-
1
def stale?(record_or_options, additional_options = {})
-
fresh_when(record_or_options, additional_options)
-
!request.fresh?(response)
-
end
-
-
# Sets a HTTP 1.1 Cache-Control header. Defaults to issuing a +private+
-
# instruction, so that intermediate caches must not cache the response.
-
#
-
# expires_in 20.minutes
-
# expires_in 3.hours, public: true
-
# expires_in 3.hours, public: true, must_revalidate: true
-
#
-
# This method will overwrite an existing Cache-Control header.
-
# See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html for more possibilities.
-
#
-
# The method will also ensure a HTTP Date header for client compatibility.
-
1
def expires_in(seconds, options = {})
-
response.cache_control.merge!(
-
:max_age => seconds,
-
:public => options.delete(:public),
-
:must_revalidate => options.delete(:must_revalidate)
-
)
-
options.delete(:private)
-
-
response.cache_control[:extras] = options.map {|k,v| "#{k}=#{v}"}
-
response.date = Time.now unless response.date?
-
end
-
-
# Sets a HTTP 1.1 Cache-Control header of <tt>no-cache</tt> so no caching should
-
# occur by the browser or intermediate caches (like caching proxy servers).
-
1
def expires_now
-
response.cache_control.replace(:no_cache => true)
-
end
-
-
1
private
-
1
def combine_etags(etag)
-
[ etag, *etaggers.map { |etagger| instance_exec(&etagger) }.compact ]
-
end
-
end
-
end
-
1
module ActionController #:nodoc:
-
1
module Cookies
-
1
extend ActiveSupport::Concern
-
-
1
include RackDelegation
-
-
1
included do
-
1
helper_method :cookies
-
end
-
-
1
private
-
1
def cookies
-
request.cookie_jar
-
end
-
end
-
end
-
1
require 'action_controller/metal/exceptions'
-
-
1
module ActionController #:nodoc:
-
# Methods for sending arbitrary data and for streaming files to the browser,
-
# instead of rendering.
-
1
module DataStreaming
-
1
extend ActiveSupport::Concern
-
-
1
include ActionController::Rendering
-
-
1
DEFAULT_SEND_FILE_TYPE = 'application/octet-stream'.freeze #:nodoc:
-
1
DEFAULT_SEND_FILE_DISPOSITION = 'attachment'.freeze #:nodoc:
-
-
1
protected
-
# Sends the file. This uses a server-appropriate method (such as X-Sendfile)
-
# via the Rack::Sendfile middleware. The header to use is set via
-
# +config.action_dispatch.x_sendfile_header+.
-
# Your server can also configure this for you by setting the X-Sendfile-Type header.
-
#
-
# Be careful to sanitize the path parameter if it is coming from a web
-
# page. <tt>send_file(params[:path])</tt> allows a malicious user to
-
# download any file on your server.
-
#
-
# Options:
-
# * <tt>:filename</tt> - suggests a filename for the browser to use.
-
# Defaults to <tt>File.basename(path)</tt>.
-
# * <tt>:type</tt> - specifies an HTTP content type.
-
# You can specify either a string or a symbol for a registered type register with
-
# <tt>Mime::Type.register</tt>, for example :json
-
# If omitted, type will be guessed from the file extension specified in <tt>:filename</tt>.
-
# If no content type is registered for the extension, default type 'application/octet-stream' will be used.
-
# * <tt>:disposition</tt> - specifies whether the file will be shown inline or downloaded.
-
# Valid values are 'inline' and 'attachment' (default).
-
# * <tt>:status</tt> - specifies the status code to send with the response. Defaults to 200.
-
# * <tt>:url_based_filename</tt> - set to +true+ if you want the browser guess the filename from
-
# the URL, which is necessary for i18n filenames on certain browsers
-
# (setting <tt>:filename</tt> overrides this option).
-
#
-
# The default Content-Type and Content-Disposition headers are
-
# set to download arbitrary binary files in as many browsers as
-
# possible. IE versions 4, 5, 5.5, and 6 are all known to have
-
# a variety of quirks (especially when downloading over SSL).
-
#
-
# Simple download:
-
#
-
# send_file '/path/to.zip'
-
#
-
# Show a JPEG in the browser:
-
#
-
# send_file '/path/to.jpeg', type: 'image/jpeg', disposition: 'inline'
-
#
-
# Show a 404 page in the browser:
-
#
-
# send_file '/path/to/404.html', type: 'text/html; charset=utf-8', status: 404
-
#
-
# Read about the other Content-* HTTP headers if you'd like to
-
# provide the user with more information (such as Content-Description) in
-
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.11.
-
#
-
# Also be aware that the document may be cached by proxies and browsers.
-
# The Pragma and Cache-Control headers declare how the file may be cached
-
# by intermediaries. They default to require clients to validate with
-
# the server before releasing cached responses. See
-
# http://www.mnot.net/cache_docs/ for an overview of web caching and
-
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9
-
# for the Cache-Control header spec.
-
1
def send_file(path, options = {}) #:doc:
-
raise MissingFile, "Cannot read file #{path}" unless File.file?(path) and File.readable?(path)
-
-
options[:filename] ||= File.basename(path) unless options[:url_based_filename]
-
send_file_headers! options
-
-
self.status = options[:status] || 200
-
self.content_type = options[:content_type] if options.key?(:content_type)
-
self.response_body = FileBody.new(path)
-
end
-
-
# Avoid having to pass an open file handle as the response body.
-
# Rack::Sendfile will usually intercept the response and uses
-
# the path directly, so there is no reason to open the file.
-
1
class FileBody #:nodoc:
-
1
attr_reader :to_path
-
-
1
def initialize(path)
-
@to_path = path
-
end
-
-
# Stream the file's contents if Rack::Sendfile isn't present.
-
1
def each
-
File.open(to_path, 'rb') do |file|
-
while chunk = file.read(16384)
-
yield chunk
-
end
-
end
-
end
-
end
-
-
# Sends the given binary data to the browser. This method is similar to
-
# <tt>render text: data</tt>, but also allows you to specify whether
-
# the browser should display the response as a file attachment (i.e. in a
-
# download dialog) or as inline data. You may also set the content type,
-
# the apparent file name, and other things.
-
#
-
# Options:
-
# * <tt>:filename</tt> - suggests a filename for the browser to use.
-
# * <tt>:type</tt> - specifies an HTTP content type. Defaults to 'application/octet-stream'. You can specify
-
# either a string or a symbol for a registered type register with <tt>Mime::Type.register</tt>, for example :json
-
# If omitted, type will be guessed from the file extension specified in <tt>:filename</tt>.
-
# If no content type is registered for the extension, default type 'application/octet-stream' will be used.
-
# * <tt>:disposition</tt> - specifies whether the file will be shown inline or downloaded.
-
# Valid values are 'inline' and 'attachment' (default).
-
# * <tt>:status</tt> - specifies the status code to send with the response. Defaults to 200.
-
#
-
# Generic data download:
-
#
-
# send_data buffer
-
#
-
# Download a dynamically-generated tarball:
-
#
-
# send_data generate_tgz('dir'), filename: 'dir.tgz'
-
#
-
# Display an image Active Record in the browser:
-
#
-
# send_data image.data, type: image.content_type, disposition: 'inline'
-
#
-
# See +send_file+ for more information on HTTP Content-* headers and caching.
-
1
def send_data(data, options = {}) #:doc:
-
send_file_headers! options
-
render options.slice(:status, :content_type).merge(:text => data)
-
end
-
-
1
private
-
1
def send_file_headers!(options)
-
type_provided = options.has_key?(:type)
-
-
content_type = options.fetch(:type, DEFAULT_SEND_FILE_TYPE)
-
raise ArgumentError, ":type option required" if content_type.nil?
-
-
if content_type.is_a?(Symbol)
-
extension = Mime[content_type]
-
raise ArgumentError, "Unknown MIME type #{options[:type]}" unless extension
-
self.content_type = extension
-
else
-
if !type_provided && options[:filename]
-
# If type wasn't provided, try guessing from file extension.
-
content_type = Mime::Type.lookup_by_extension(File.extname(options[:filename]).downcase.delete('.')) || content_type
-
end
-
self.content_type = content_type
-
end
-
-
disposition = options.fetch(:disposition, DEFAULT_SEND_FILE_DISPOSITION)
-
unless disposition.nil?
-
disposition += %(; filename="#{options[:filename]}") if options[:filename]
-
headers['Content-Disposition'] = disposition
-
end
-
-
headers['Content-Transfer-Encoding'] = 'binary'
-
-
response.sending_file = true
-
-
# Fix a problem with IE 6.0 on opening downloaded files:
-
# If Cache-Control: no-cache is set (which Rails does by default),
-
# IE removes the file it just downloaded from its cache immediately
-
# after it displays the "open/save" dialog, which means that if you
-
# hit "open" the file isn't there anymore when the application that
-
# is called for handling the download is run, so let's workaround that
-
response.cache_control[:public] ||= false
-
end
-
end
-
end
-
1
module ActionController
-
1
class ActionControllerError < StandardError #:nodoc:
-
end
-
-
1
class BadRequest < ActionControllerError #:nodoc:
-
1
attr_reader :original_exception
-
-
1
def initialize(type = nil, e = nil)
-
return super() unless type && e
-
-
super("Invalid #{type} parameters: #{e.message}")
-
@original_exception = e
-
set_backtrace e.backtrace
-
end
-
end
-
-
1
class RenderError < ActionControllerError #:nodoc:
-
end
-
-
1
class RoutingError < ActionControllerError #:nodoc:
-
1
attr_reader :failures
-
1
def initialize(message, failures=[])
-
super(message)
-
@failures = failures
-
end
-
end
-
-
1
class ActionController::UrlGenerationError < RoutingError #:nodoc:
-
end
-
-
1
class MethodNotAllowed < ActionControllerError #:nodoc:
-
1
def initialize(*allowed_methods)
-
super("Only #{allowed_methods.to_sentence(:locale => :en)} requests are allowed.")
-
end
-
end
-
-
1
class NotImplemented < MethodNotAllowed #:nodoc:
-
end
-
-
1
class UnknownController < ActionControllerError #:nodoc:
-
end
-
-
1
class MissingFile < ActionControllerError #:nodoc:
-
end
-
-
1
class SessionOverflowError < ActionControllerError #:nodoc:
-
1
DEFAULT_MESSAGE = 'Your session data is larger than the data column in which it is to be stored. You must increase the size of your data column if you intend to store large data.'
-
-
1
def initialize(message = nil)
-
super(message || DEFAULT_MESSAGE)
-
end
-
end
-
-
1
class UnknownHttpMethod < ActionControllerError #:nodoc:
-
end
-
-
1
class UnknownFormat < ActionControllerError #:nodoc:
-
end
-
end
-
1
module ActionController #:nodoc:
-
1
module Flash
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
1
class_attribute :_flash_types, instance_accessor: false
-
1
self._flash_types = []
-
-
1
delegate :flash, to: :request
-
1
add_flash_types(:alert, :notice)
-
end
-
-
1
module ClassMethods
-
1
def add_flash_types(*types)
-
1
types.each do |type|
-
2
next if _flash_types.include?(type)
-
-
2
define_method(type) do
-
request.flash[type]
-
end
-
2
helper_method type
-
-
2
_flash_types << type
-
end
-
end
-
end
-
-
1
protected
-
1
def redirect_to(options = {}, response_status_and_flash = {}) #:doc:
-
self.class._flash_types.each do |flash_type|
-
if type = response_status_and_flash.delete(flash_type)
-
flash[flash_type] = type
-
end
-
end
-
-
if other_flashes = response_status_and_flash.delete(:flash)
-
flash.update(other_flashes)
-
end
-
-
super(options, response_status_and_flash)
-
end
-
end
-
end
-
1
module ActionController
-
# This module provides a method which will redirect browser to use HTTPS
-
# protocol. This will ensure that user's sensitive information will be
-
# transferred safely over the internet. You _should_ always force browser
-
# to use HTTPS when you're transferring sensitive information such as
-
# user authentication, account information, or credit card information.
-
#
-
# Note that if you are really concerned about your application security,
-
# you might consider using +config.force_ssl+ in your config file instead.
-
# That will ensure all the data transferred via HTTPS protocol and prevent
-
# user from getting session hijacked when accessing the site under unsecured
-
# HTTP protocol.
-
1
module ForceSSL
-
1
extend ActiveSupport::Concern
-
1
include AbstractController::Callbacks
-
-
1
module ClassMethods
-
# Force the request to this particular controller or specified actions to be
-
# under HTTPS protocol.
-
#
-
# If you need to disable this for any reason (e.g. development) then you can use
-
# an +:if+ or +:unless+ condition.
-
#
-
# class AccountsController < ApplicationController
-
# force_ssl if: :ssl_configured?
-
#
-
# def ssl_configured?
-
# !Rails.env.development?
-
# end
-
# end
-
#
-
# ==== Options
-
# * <tt>host</tt> - Redirect to a different host name
-
# * <tt>only</tt> - The callback should be run only for this action
-
# * <tt>except</tt> - The callback should be run for all actions except this action
-
# * <tt>if</tt> - A symbol naming an instance method or a proc; the callback
-
# will be called only when it returns a true value.
-
# * <tt>unless</tt> - A symbol naming an instance method or a proc; the callback
-
# will be called only when it returns a false value.
-
1
def force_ssl(options = {})
-
host = options.delete(:host)
-
before_filter(options) do
-
force_ssl_redirect(host)
-
end
-
end
-
end
-
-
# Redirect the existing request to use the HTTPS protocol.
-
#
-
# ==== Parameters
-
# * <tt>host</tt> - Redirect to a different host name
-
1
def force_ssl_redirect(host = nil)
-
unless request.ssl?
-
redirect_options = {:protocol => 'https://', :status => :moved_permanently}
-
redirect_options.merge!(:host => host) if host
-
redirect_options.merge!(:params => request.query_parameters)
-
flash.keep if respond_to?(:flash)
-
redirect_to redirect_options
-
end
-
end
-
end
-
end
-
1
module ActionController
-
1
module Head
-
1
extend ActiveSupport::Concern
-
-
# Return a response that has no content (merely headers). The options
-
# argument is interpreted to be a hash of header names and values.
-
# This allows you to easily return a response that consists only of
-
# significant headers:
-
#
-
# head :created, location: person_path(@person)
-
#
-
# head :created, location: @person
-
#
-
# It can also be used to return exceptional conditions:
-
#
-
# return head(:method_not_allowed) unless request.post?
-
# return head(:bad_request) unless valid_request?
-
# render
-
1
def head(status, options = {})
-
options, status = status, nil if status.is_a?(Hash)
-
status ||= options.delete(:status) || :ok
-
location = options.delete(:location)
-
content_type = options.delete(:content_type)
-
-
options.each do |key, value|
-
headers[key.to_s.dasherize.split('-').each { |v| v[0] = v[0].chr.upcase }.join('-')] = value.to_s
-
end
-
-
self.status = status
-
self.location = url_for(location) if location
-
-
if include_content?(self.status)
-
self.content_type = content_type || (Mime[formats.first] if formats)
-
self.response_body = " "
-
else
-
headers.delete('Content-Type')
-
headers.delete('Content-Length')
-
self.response_body = ""
-
end
-
end
-
-
1
private
-
# :nodoc:
-
1
def include_content?(status)
-
case status
-
when 100..199
-
false
-
when 204, 205, 304
-
false
-
else
-
true
-
end
-
end
-
end
-
end
-
-
1
module ActionController
-
# The \Rails framework provides a large number of helpers for working with assets, dates, forms,
-
# numbers and model objects, to name a few. These helpers are available to all templates
-
# by default.
-
#
-
# In addition to using the standard template helpers provided, creating custom helpers to
-
# extract complicated logic or reusable functionality is strongly encouraged. By default, each controller
-
# will include all helpers.
-
#
-
# In previous versions of \Rails the controller will include a helper whose
-
# name matches that of the controller, e.g., <tt>MyController</tt> will automatically
-
# include <tt>MyHelper</tt>. To return old behavior set +config.action_controller.include_all_helpers+ to +false+.
-
#
-
# Additional helpers can be specified using the +helper+ class method in ActionController::Base or any
-
# controller which inherits from it.
-
#
-
# The +to_s+ method from the \Time class can be wrapped in a helper method to display a custom message if
-
# a \Time object is blank:
-
#
-
# module FormattedTimeHelper
-
# def format_time(time, format=:long, blank_message=" ")
-
# time.blank? ? blank_message : time.to_s(format)
-
# end
-
# end
-
#
-
# FormattedTimeHelper can now be included in a controller, using the +helper+ class method:
-
#
-
# class EventsController < ActionController::Base
-
# helper FormattedTimeHelper
-
# def index
-
# @events = Event.all
-
# end
-
# end
-
#
-
# Then, in any view rendered by <tt>EventController</tt>, the <tt>format_time</tt> method can be called:
-
#
-
# <% @events.each do |event| -%>
-
# <p>
-
# <%= format_time(event.time, :short, "N/A") %> | <%= event.name %>
-
# </p>
-
# <% end -%>
-
#
-
# Finally, assuming we have two event instances, one which has a time and one which does not,
-
# the output might look like this:
-
#
-
# 23 Aug 11:30 | Carolina Railhawks Soccer Match
-
# N/A | Carolina Railhaws Training Workshop
-
#
-
1
module Helpers
-
1
extend ActiveSupport::Concern
-
-
2
class << self; attr_accessor :helpers_path; end
-
1
include AbstractController::Helpers
-
-
1
included do
-
1
class_attribute :helpers_path, :include_all_helpers
-
1
self.helpers_path ||= []
-
1
self.include_all_helpers = true
-
end
-
-
1
module ClassMethods
-
# Declares helper accessors for controller attributes. For example, the
-
# following adds new +name+ and <tt>name=</tt> instance methods to a
-
# controller and makes them available to the view:
-
# attr_accessor :name
-
# helper_attr :name
-
#
-
# ==== Parameters
-
# * <tt>attrs</tt> - Names of attributes to be converted into helpers.
-
1
def helper_attr(*attrs)
-
attrs.flatten.each { |attr| helper_method(attr, "#{attr}=") }
-
end
-
-
# Provides a proxy to access helpers methods from outside the view.
-
1
def helpers
-
@helper_proxy ||= ActionView::Base.new.extend(_helpers)
-
end
-
-
# Overwrite modules_for_helpers to accept :all as argument, which loads
-
# all helpers in helpers_path.
-
#
-
# ==== Parameters
-
# * <tt>args</tt> - A list of helpers
-
#
-
# ==== Returns
-
# * <tt>array</tt> - A normalized list of modules for the list of helpers provided.
-
1
def modules_for_helpers(args)
-
2
args += all_application_helpers if args.delete(:all)
-
2
super(args)
-
end
-
-
1
def all_helpers_from_path(path)
-
helpers = []
-
Array(path).each do |_path|
-
extract = /^#{Regexp.quote(_path.to_s)}\/?(.*)_helper.rb$/
-
names = Dir["#{_path}/**/*_helper.rb"].map { |file| file.sub(extract, '\1') }
-
helpers += names.sort
-
end
-
helpers.uniq!
-
helpers
-
end
-
-
1
private
-
# Extract helper names from files in <tt>app/helpers/**/*_helper.rb</tt>
-
1
def all_application_helpers
-
all_helpers_from_path(helpers_path)
-
end
-
end
-
end
-
end
-
-
1
module ActionController
-
# Adds the ability to prevent public methods on a controller to be called as actions.
-
1
module HideActions
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
1
class_attribute :hidden_actions
-
1
self.hidden_actions = Set.new.freeze
-
end
-
-
1
private
-
-
# Overrides AbstractController::Base#action_method? to return false if the
-
# action name is in the list of hidden actions.
-
1
def method_for_action(action_name)
-
1
self.class.visible_action?(action_name) && super
-
end
-
-
1
module ClassMethods
-
# Sets all of the actions passed in as hidden actions.
-
#
-
# ==== Parameters
-
# * <tt>args</tt> - A list of actions
-
1
def hide_action(*args)
-
self.hidden_actions = hidden_actions.dup.merge(args.map(&:to_s)).freeze
-
end
-
-
1
def visible_action?(action_name)
-
1
action_methods.include?(action_name)
-
end
-
-
# Overrides AbstractController::Base#action_methods to remove any methods
-
# that are listed as hidden methods.
-
1
def action_methods
-
2
@action_methods ||= Set.new(super.reject { |name| hidden_actions.include?(name) }).freeze
-
end
-
end
-
end
-
end
-
1
require 'base64'
-
-
1
module ActionController
-
# Makes it dead easy to do HTTP Basic, Digest and Token authentication.
-
1
module HttpAuthentication
-
# Makes it dead easy to do HTTP \Basic authentication.
-
#
-
# === Simple \Basic example
-
#
-
# class PostsController < ApplicationController
-
# http_basic_authenticate_with name: "dhh", password: "secret", except: :index
-
#
-
# def index
-
# render text: "Everyone can see me!"
-
# end
-
#
-
# def edit
-
# render text: "I'm only accessible if you know the password"
-
# end
-
# end
-
#
-
# === Advanced \Basic example
-
#
-
# Here is a more advanced \Basic example where only Atom feeds and the XML API is protected by HTTP authentication,
-
# the regular HTML interface is protected by a session approach:
-
#
-
# class ApplicationController < ActionController::Base
-
# before_filter :set_account, :authenticate
-
#
-
# protected
-
# def set_account
-
# @account = Account.find_by_url_name(request.subdomains.first)
-
# end
-
#
-
# def authenticate
-
# case request.format
-
# when Mime::XML, Mime::ATOM
-
# if user = authenticate_with_http_basic { |u, p| @account.users.authenticate(u, p) }
-
# @current_user = user
-
# else
-
# request_http_basic_authentication
-
# end
-
# else
-
# if session_authenticated?
-
# @current_user = @account.users.find(session[:authenticated][:user_id])
-
# else
-
# redirect_to(login_url) and return false
-
# end
-
# end
-
# end
-
# end
-
#
-
# In your integration tests, you can do something like this:
-
#
-
# def test_access_granted_from_xml
-
# get(
-
# "/notes/1.xml", nil,
-
# 'HTTP_AUTHORIZATION' => ActionController::HttpAuthentication::Basic.encode_credentials(users(:dhh).name, users(:dhh).password)
-
# )
-
#
-
# assert_equal 200, status
-
# end
-
1
module Basic
-
1
extend self
-
-
1
module ControllerMethods
-
1
extend ActiveSupport::Concern
-
-
1
module ClassMethods
-
1
def http_basic_authenticate_with(options = {})
-
before_filter(options.except(:name, :password, :realm)) do
-
authenticate_or_request_with_http_basic(options[:realm] || "Application") do |name, password|
-
name == options[:name] && password == options[:password]
-
end
-
end
-
end
-
end
-
-
1
def authenticate_or_request_with_http_basic(realm = "Application", &login_procedure)
-
authenticate_with_http_basic(&login_procedure) || request_http_basic_authentication(realm)
-
end
-
-
1
def authenticate_with_http_basic(&login_procedure)
-
HttpAuthentication::Basic.authenticate(request, &login_procedure)
-
end
-
-
1
def request_http_basic_authentication(realm = "Application")
-
HttpAuthentication::Basic.authentication_request(self, realm)
-
end
-
end
-
-
1
def authenticate(request, &login_procedure)
-
unless request.authorization.blank?
-
login_procedure.call(*user_name_and_password(request))
-
end
-
end
-
-
1
def user_name_and_password(request)
-
decode_credentials(request).split(/:/, 2)
-
end
-
-
1
def decode_credentials(request)
-
::Base64.decode64(request.authorization.split(' ', 2).last || '')
-
end
-
-
1
def encode_credentials(user_name, password)
-
"Basic #{::Base64.strict_encode64("#{user_name}:#{password}")}"
-
end
-
-
1
def authentication_request(controller, realm)
-
controller.headers["WWW-Authenticate"] = %(Basic realm="#{realm.gsub(/"/, "")}")
-
controller.response_body = "HTTP Basic: Access denied.\n"
-
controller.status = 401
-
end
-
end
-
-
# Makes it dead easy to do HTTP \Digest authentication.
-
#
-
# === Simple \Digest example
-
#
-
# require 'digest/md5'
-
# class PostsController < ApplicationController
-
# REALM = "SuperSecret"
-
# USERS = {"dhh" => "secret", #plain text password
-
# "dap" => Digest::MD5.hexdigest(["dap",REALM,"secret"].join(":"))} #ha1 digest password
-
#
-
# before_filter :authenticate, except: [:index]
-
#
-
# def index
-
# render text: "Everyone can see me!"
-
# end
-
#
-
# def edit
-
# render text: "I'm only accessible if you know the password"
-
# end
-
#
-
# private
-
# def authenticate
-
# authenticate_or_request_with_http_digest(REALM) do |username|
-
# USERS[username]
-
# end
-
# end
-
# end
-
#
-
# === Notes
-
#
-
# The +authenticate_or_request_with_http_digest+ block must return the user's password
-
# or the ha1 digest hash so the framework can appropriately hash to check the user's
-
# credentials. Returning +nil+ will cause authentication to fail.
-
#
-
# Storing the ha1 hash: MD5(username:realm:password), is better than storing a plain password. If
-
# the password file or database is compromised, the attacker would be able to use the ha1 hash to
-
# authenticate as the user at this +realm+, but would not have the user's password to try using at
-
# other sites.
-
#
-
# In rare instances, web servers or front proxies strip authorization headers before
-
# they reach your application. You can debug this situation by logging all environment
-
# variables, and check for HTTP_AUTHORIZATION, amongst others.
-
1
module Digest
-
1
extend self
-
-
1
module ControllerMethods
-
1
def authenticate_or_request_with_http_digest(realm = "Application", &password_procedure)
-
authenticate_with_http_digest(realm, &password_procedure) || request_http_digest_authentication(realm)
-
end
-
-
# Authenticate with HTTP Digest, returns true or false
-
1
def authenticate_with_http_digest(realm = "Application", &password_procedure)
-
HttpAuthentication::Digest.authenticate(request, realm, &password_procedure)
-
end
-
-
# Render output including the HTTP Digest authentication header
-
1
def request_http_digest_authentication(realm = "Application", message = nil)
-
HttpAuthentication::Digest.authentication_request(self, realm, message)
-
end
-
end
-
-
# Returns false on a valid response, true otherwise
-
1
def authenticate(request, realm, &password_procedure)
-
request.authorization && validate_digest_response(request, realm, &password_procedure)
-
end
-
-
# Returns false unless the request credentials response value matches the expected value.
-
# First try the password as a ha1 digest password. If this fails, then try it as a plain
-
# text password.
-
1
def validate_digest_response(request, realm, &password_procedure)
-
secret_key = secret_token(request)
-
credentials = decode_credentials_header(request)
-
valid_nonce = validate_nonce(secret_key, request, credentials[:nonce])
-
-
if valid_nonce && realm == credentials[:realm] && opaque(secret_key) == credentials[:opaque]
-
password = password_procedure.call(credentials[:username])
-
return false unless password
-
-
method = request.env['rack.methodoverride.original_method'] || request.env['REQUEST_METHOD']
-
uri = credentials[:uri]
-
-
[true, false].any? do |trailing_question_mark|
-
[true, false].any? do |password_is_ha1|
-
_uri = trailing_question_mark ? uri + "?" : uri
-
expected = expected_response(method, _uri, credentials, password, password_is_ha1)
-
expected == credentials[:response]
-
end
-
end
-
end
-
end
-
-
# Returns the expected response for a request of +http_method+ to +uri+ with the decoded +credentials+ and the expected +password+
-
# Optional parameter +password_is_ha1+ is set to +true+ by default, since best practice is to store ha1 digest instead
-
# of a plain-text password.
-
1
def expected_response(http_method, uri, credentials, password, password_is_ha1=true)
-
ha1 = password_is_ha1 ? password : ha1(credentials, password)
-
ha2 = ::Digest::MD5.hexdigest([http_method.to_s.upcase, uri].join(':'))
-
::Digest::MD5.hexdigest([ha1, credentials[:nonce], credentials[:nc], credentials[:cnonce], credentials[:qop], ha2].join(':'))
-
end
-
-
1
def ha1(credentials, password)
-
::Digest::MD5.hexdigest([credentials[:username], credentials[:realm], password].join(':'))
-
end
-
-
1
def encode_credentials(http_method, credentials, password, password_is_ha1)
-
credentials[:response] = expected_response(http_method, credentials[:uri], credentials, password, password_is_ha1)
-
"Digest " + credentials.sort_by {|x| x[0].to_s }.map {|v| "#{v[0]}='#{v[1]}'" }.join(', ')
-
end
-
-
1
def decode_credentials_header(request)
-
decode_credentials(request.authorization)
-
end
-
-
1
def decode_credentials(header)
-
HashWithIndifferentAccess[header.to_s.gsub(/^Digest\s+/,'').split(',').map do |pair|
-
key, value = pair.split('=', 2)
-
[key.strip, value.to_s.gsub(/^"|"$/,'').delete('\'')]
-
end]
-
end
-
-
1
def authentication_header(controller, realm)
-
secret_key = secret_token(controller.request)
-
nonce = self.nonce(secret_key)
-
opaque = opaque(secret_key)
-
controller.headers["WWW-Authenticate"] = %(Digest realm="#{realm}", qop="auth", algorithm=MD5, nonce="#{nonce}", opaque="#{opaque}")
-
end
-
-
1
def authentication_request(controller, realm, message = nil)
-
message ||= "HTTP Digest: Access denied.\n"
-
authentication_header(controller, realm)
-
controller.response_body = message
-
controller.status = 401
-
end
-
-
1
def secret_token(request)
-
secret = request.env["action_dispatch.secret_token"]
-
raise "You must set config.secret_token in your app's config" if secret.blank?
-
secret
-
end
-
-
# Uses an MD5 digest based on time to generate a value to be used only once.
-
#
-
# A server-specified data string which should be uniquely generated each time a 401 response is made.
-
# It is recommended that this string be base64 or hexadecimal data.
-
# Specifically, since the string is passed in the header lines as a quoted string, the double-quote character is not allowed.
-
#
-
# The contents of the nonce are implementation dependent.
-
# The quality of the implementation depends on a good choice.
-
# A nonce might, for example, be constructed as the base 64 encoding of
-
#
-
# time-stamp H(time-stamp ":" ETag ":" private-key)
-
#
-
# where time-stamp is a server-generated time or other non-repeating value,
-
# ETag is the value of the HTTP ETag header associated with the requested entity,
-
# and private-key is data known only to the server.
-
# With a nonce of this form a server would recalculate the hash portion after receiving the client authentication header and
-
# reject the request if it did not match the nonce from that header or
-
# if the time-stamp value is not recent enough. In this way the server can limit the time of the nonce's validity.
-
# The inclusion of the ETag prevents a replay request for an updated version of the resource.
-
# (Note: including the IP address of the client in the nonce would appear to offer the server the ability
-
# to limit the reuse of the nonce to the same client that originally got it.
-
# However, that would break proxy farms, where requests from a single user often go through different proxies in the farm.
-
# Also, IP address spoofing is not that hard.)
-
#
-
# An implementation might choose not to accept a previously used nonce or a previously used digest, in order to
-
# protect against a replay attack. Or, an implementation might choose to use one-time nonces or digests for
-
# POST, PUT, or PATCH requests and a time-stamp for GET requests. For more details on the issues involved see Section 4
-
# of this document.
-
#
-
# The nonce is opaque to the client. Composed of Time, and hash of Time with secret
-
# key from the Rails session secret generated upon creation of project. Ensures
-
# the time cannot be modified by client.
-
1
def nonce(secret_key, time = Time.now)
-
t = time.to_i
-
hashed = [t, secret_key]
-
digest = ::Digest::MD5.hexdigest(hashed.join(":"))
-
::Base64.strict_encode64("#{t}:#{digest}")
-
end
-
-
# Might want a shorter timeout depending on whether the request
-
# is a PATCH, PUT, or POST, and if client is browser or web service.
-
# Can be much shorter if the Stale directive is implemented. This would
-
# allow a user to use new nonce without prompting user again for their
-
# username and password.
-
1
def validate_nonce(secret_key, request, value, seconds_to_timeout=5*60)
-
t = ::Base64.decode64(value).split(":").first.to_i
-
nonce(secret_key, t) == value && (t - Time.now.to_i).abs <= seconds_to_timeout
-
end
-
-
# Opaque based on random generation - but changing each request?
-
1
def opaque(secret_key)
-
::Digest::MD5.hexdigest(secret_key)
-
end
-
-
end
-
-
# Makes it dead easy to do HTTP Token authentication.
-
#
-
# Simple Token example:
-
#
-
# class PostsController < ApplicationController
-
# TOKEN = "secret"
-
#
-
# before_filter :authenticate, except: [ :index ]
-
#
-
# def index
-
# render text: "Everyone can see me!"
-
# end
-
#
-
# def edit
-
# render text: "I'm only accessible if you know the password"
-
# end
-
#
-
# private
-
# def authenticate
-
# authenticate_or_request_with_http_token do |token, options|
-
# token == TOKEN
-
# end
-
# end
-
# end
-
#
-
#
-
# Here is a more advanced Token example where only Atom feeds and the XML API is protected by HTTP token authentication,
-
# the regular HTML interface is protected by a session approach:
-
#
-
# class ApplicationController < ActionController::Base
-
# before_filter :set_account, :authenticate
-
#
-
# protected
-
# def set_account
-
# @account = Account.find_by_url_name(request.subdomains.first)
-
# end
-
#
-
# def authenticate
-
# case request.format
-
# when Mime::XML, Mime::ATOM
-
# if user = authenticate_with_http_token { |t, o| @account.users.authenticate(t, o) }
-
# @current_user = user
-
# else
-
# request_http_token_authentication
-
# end
-
# else
-
# if session_authenticated?
-
# @current_user = @account.users.find(session[:authenticated][:user_id])
-
# else
-
# redirect_to(login_url) and return false
-
# end
-
# end
-
# end
-
# end
-
#
-
#
-
# In your integration tests, you can do something like this:
-
#
-
# def test_access_granted_from_xml
-
# get(
-
# "/notes/1.xml", nil,
-
# 'HTTP_AUTHORIZATION' => ActionController::HttpAuthentication::Token.encode_credentials(users(:dhh).token)
-
# )
-
#
-
# assert_equal 200, status
-
# end
-
#
-
#
-
# On shared hosts, Apache sometimes doesn't pass authentication headers to
-
# FCGI instances. If your environment matches this description and you cannot
-
# authenticate, try this rule in your Apache setup:
-
#
-
# RewriteRule ^(.*)$ dispatch.fcgi [E=X-HTTP_AUTHORIZATION:%{HTTP:Authorization},QSA,L]
-
1
module Token
-
1
extend self
-
-
1
module ControllerMethods
-
1
def authenticate_or_request_with_http_token(realm = "Application", &login_procedure)
-
authenticate_with_http_token(&login_procedure) || request_http_token_authentication(realm)
-
end
-
-
1
def authenticate_with_http_token(&login_procedure)
-
Token.authenticate(self, &login_procedure)
-
end
-
-
1
def request_http_token_authentication(realm = "Application")
-
Token.authentication_request(self, realm)
-
end
-
end
-
-
# If token Authorization header is present, call the login
-
# procedure with the present token and options.
-
#
-
# [controller]
-
# ActionController::Base instance for the current request.
-
#
-
# [login_procedure]
-
# Proc to call if a token is present. The Proc should take two arguments:
-
#
-
# authenticate(controller) { |token, options| ... }
-
#
-
# Returns the return value of <tt>login_procedure</tt> if a
-
# token is found. Returns <tt>nil</tt> if no token is found.
-
-
1
def authenticate(controller, &login_procedure)
-
token, options = token_and_options(controller.request)
-
unless token.blank?
-
login_procedure.call(token, options)
-
end
-
end
-
-
# Parses the token and options out of the token authorization header. If
-
# the header looks like this:
-
# Authorization: Token token="abc", nonce="def"
-
# Then the returned token is "abc", and the options is {nonce: "def"}
-
#
-
# request - ActionDispatch::Request instance with the current headers.
-
#
-
# Returns an Array of [String, Hash] if a token is present.
-
# Returns nil if no token is found.
-
1
def token_and_options(request)
-
if request.authorization.to_s[/^Token (.*)/]
-
values = Hash[$1.split(',').map do |value|
-
value.strip! # remove any spaces between commas and values
-
key, value = value.split(/\=\"?/) # split key=value pairs
-
if value
-
value.chomp!('"') # chomp trailing " in value
-
value.gsub!(/\\\"/, '"') # unescape remaining quotes
-
[key, value]
-
end
-
end.compact]
-
[values.delete("token"), values.with_indifferent_access]
-
end
-
end
-
-
# Encodes the given token and options into an Authorization header value.
-
#
-
# token - String token.
-
# options - optional Hash of the options.
-
#
-
# Returns String.
-
1
def encode_credentials(token, options = {})
-
values = ["token=#{token.to_s.inspect}"] + options.map do |key, value|
-
"#{key}=#{value.to_s.inspect}"
-
end
-
"Token #{values * ", "}"
-
end
-
-
# Sets a WWW-Authenticate to let the client know a token is desired.
-
#
-
# controller - ActionController::Base instance for the outgoing response.
-
# realm - String realm to use in the header.
-
#
-
# Returns nothing.
-
1
def authentication_request(controller, realm)
-
controller.headers["WWW-Authenticate"] = %(Token realm="#{realm.gsub(/"/, "")}")
-
controller.__send__ :render, :text => "HTTP Token: Access denied.\n", :status => :unauthorized
-
end
-
end
-
end
-
end
-
1
module ActionController
-
1
module ImplicitRender
-
1
def send_action(method, *args)
-
1
ret = super
-
1
default_render unless performed?
-
1
ret
-
end
-
-
1
def default_render(*args)
-
render(*args)
-
end
-
-
1
def method_for_action(action_name)
-
super || if template_exists?(action_name.to_s, _prefixes)
-
"default_render"
-
1
end
-
end
-
end
-
end
-
1
require 'benchmark'
-
1
require 'abstract_controller/logger'
-
-
1
module ActionController
-
# Adds instrumentation to several ends in ActionController::Base. It also provides
-
# some hooks related with process_action, this allows an ORM like Active Record
-
# and/or DataMapper to plug in ActionController and show related information.
-
#
-
# Check ActiveRecord::Railties::ControllerRuntime for an example.
-
1
module Instrumentation
-
1
extend ActiveSupport::Concern
-
-
1
include AbstractController::Logger
-
1
include ActionController::RackDelegation
-
-
1
attr_internal :view_runtime
-
-
1
def process_action(*args)
-
1
raw_payload = {
-
:controller => self.class.name,
-
:action => self.action_name,
-
:params => request.filtered_parameters,
-
:format => request.format.try(:ref),
-
:method => request.method,
-
1
:path => (request.fullpath rescue "unknown")
-
}
-
-
1
ActiveSupport::Notifications.instrument("start_processing.action_controller", raw_payload.dup)
-
-
1
ActiveSupport::Notifications.instrument("process_action.action_controller", raw_payload) do |payload|
-
1
result = super
-
1
payload[:status] = response.status
-
1
append_info_to_payload(payload)
-
1
result
-
end
-
end
-
-
1
def render(*args)
-
1
render_output = nil
-
1
self.view_runtime = cleanup_view_runtime do
-
2
Benchmark.ms { render_output = super }
-
end
-
1
render_output
-
end
-
-
1
def send_file(path, options={})
-
ActiveSupport::Notifications.instrument("send_file.action_controller",
-
options.merge(:path => path)) do
-
super
-
end
-
end
-
-
1
def send_data(data, options = {})
-
ActiveSupport::Notifications.instrument("send_data.action_controller", options) do
-
super
-
end
-
end
-
-
1
def redirect_to(*args)
-
ActiveSupport::Notifications.instrument("redirect_to.action_controller") do |payload|
-
result = super
-
payload[:status] = response.status
-
payload[:location] = response.location
-
result
-
end
-
end
-
-
1
private
-
-
# A hook invoked everytime a before callback is halted.
-
1
def halted_callback_hook(filter)
-
ActiveSupport::Notifications.instrument("halted_callback.action_controller", :filter => filter)
-
end
-
-
# A hook which allows you to clean up any time taken into account in
-
# views wrongly, like database querying time.
-
#
-
# def cleanup_view_runtime
-
# super - time_taken_in_something_expensive
-
# end
-
#
-
# :api: plugin
-
1
def cleanup_view_runtime #:nodoc:
-
1
yield
-
end
-
-
# Every time after an action is processed, this method is invoked
-
# with the payload, so you can add more information.
-
# :api: plugin
-
1
def append_info_to_payload(payload) #:nodoc:
-
1
payload[:view_runtime] = view_runtime
-
end
-
-
1
module ClassMethods
-
# A hook which allows other frameworks to log what happened during
-
# controller process action. This method should return an array
-
# with the messages to be added.
-
# :api: plugin
-
1
def log_process_action(payload) #:nodoc:
-
messages, view_runtime = [], payload[:view_runtime]
-
messages << ("Views: %.1fms" % view_runtime.to_f) if view_runtime
-
messages
-
end
-
end
-
end
-
end
-
1
require 'action_dispatch/http/response'
-
1
require 'delegate'
-
-
1
module ActionController
-
# Mix this module in to your controller, and all actions in that controller
-
# will be able to stream data to the client as it's written.
-
#
-
# class MyController < ActionController::Base
-
# include ActionController::Live
-
#
-
# def stream
-
# response.headers['Content-Type'] = 'text/event-stream'
-
# 100.times {
-
# response.stream.write "hello world\n"
-
# sleep 1
-
# }
-
# response.stream.close
-
# end
-
# end
-
#
-
# There are a few caveats with this use. You *cannot* write headers after the
-
# response has been committed (Response#committed? will return truthy).
-
# Calling +write+ or +close+ on the response stream will cause the response
-
# object to be committed. Make sure all headers are set before calling write
-
# or close on your stream.
-
#
-
# You *must* call close on your stream when you're finished, otherwise the
-
# socket may be left open forever.
-
#
-
# The final caveat is that your actions are executed in a separate thread than
-
# the main thread. Make sure your actions are thread safe, and this shouldn't
-
# be a problem (don't share state across threads, etc).
-
1
module Live
-
1
class Buffer < ActionDispatch::Response::Buffer #:nodoc:
-
1
def initialize(response)
-
super(response, SizedQueue.new(10))
-
end
-
-
1
def write(string)
-
unless @response.committed?
-
@response.headers["Cache-Control"] = "no-cache"
-
@response.headers.delete "Content-Length"
-
end
-
-
super
-
end
-
-
1
def each
-
while str = @buf.pop
-
yield str
-
end
-
end
-
-
1
def close
-
super
-
@buf.push nil
-
end
-
end
-
-
1
class Response < ActionDispatch::Response #:nodoc: all
-
1
class Header < DelegateClass(Hash)
-
1
def initialize(response, header)
-
@response = response
-
super(header)
-
end
-
-
1
def []=(k,v)
-
if @response.committed?
-
raise ActionDispatch::IllegalStateError, 'header already sent'
-
end
-
-
super
-
end
-
-
1
def merge(other)
-
self.class.new @response, __getobj__.merge(other)
-
end
-
-
1
def to_hash
-
__getobj__.dup
-
end
-
end
-
-
1
def commit!
-
headers.freeze
-
super
-
end
-
-
1
private
-
-
1
def build_buffer(response, body)
-
buf = Live::Buffer.new response
-
body.each { |part| buf.write part }
-
buf
-
end
-
-
1
def merge_default_headers(original, default)
-
Header.new self, super
-
end
-
end
-
-
1
def process(name)
-
t1 = Thread.current
-
locals = t1.keys.map { |key| [key, t1[key]] }
-
-
# This processes the action in a child thread. It lets us return the
-
# response code and headers back up the rack stack, and still process
-
# the body in parallel with sending data to the client
-
Thread.new {
-
t2 = Thread.current
-
t2.abort_on_exception = true
-
-
# Since we're processing the view in a different thread, copy the
-
# thread locals from the main thread to the child thread. :'(
-
locals.each { |k,v| t2[k] = v }
-
-
begin
-
super(name)
-
ensure
-
@_response.commit!
-
end
-
}
-
-
@_response.await_commit
-
end
-
-
1
def response_body=(body)
-
super
-
response.stream.close if response
-
end
-
-
1
def set_response!(request)
-
if request.env["HTTP_VERSION"] == "HTTP/1.0"
-
super
-
else
-
@_response = Live::Response.new
-
@_response.request = request
-
end
-
end
-
end
-
end
-
1
require 'abstract_controller/collector'
-
-
1
module ActionController #:nodoc:
-
1
module MimeResponds
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
1
class_attribute :responder, :mimes_for_respond_to
-
1
self.responder = ActionController::Responder
-
1
clear_respond_to
-
end
-
-
1
module ClassMethods
-
# Defines mime types that are rendered by default when invoking
-
# <tt>respond_with</tt>.
-
#
-
# respond_to :html, :xml, :json
-
#
-
# Specifies that all actions in the controller respond to requests
-
# for <tt>:html</tt>, <tt>:xml</tt> and <tt>:json</tt>.
-
#
-
# To specify on per-action basis, use <tt>:only</tt> and
-
# <tt>:except</tt> with an array of actions or a single action:
-
#
-
# respond_to :html
-
# respond_to :xml, :json, except: [ :edit ]
-
#
-
# This specifies that all actions respond to <tt>:html</tt>
-
# and all actions except <tt>:edit</tt> respond to <tt>:xml</tt> and
-
# <tt>:json</tt>.
-
#
-
# respond_to :json, only: :create
-
#
-
# This specifies that the <tt>:create</tt> action and no other responds
-
# to <tt>:json</tt>.
-
1
def respond_to(*mimes)
-
options = mimes.extract_options!
-
-
only_actions = Array(options.delete(:only)).map(&:to_s)
-
except_actions = Array(options.delete(:except)).map(&:to_s)
-
-
new = mimes_for_respond_to.dup
-
mimes.each do |mime|
-
mime = mime.to_sym
-
new[mime] = {}
-
new[mime][:only] = only_actions unless only_actions.empty?
-
new[mime][:except] = except_actions unless except_actions.empty?
-
end
-
self.mimes_for_respond_to = new.freeze
-
end
-
-
# Clear all mime types in <tt>respond_to</tt>.
-
#
-
1
def clear_respond_to
-
1
self.mimes_for_respond_to = Hash.new.freeze
-
end
-
end
-
-
# Without web-service support, an action which collects the data for displaying a list of people
-
# might look something like this:
-
#
-
# def index
-
# @people = Person.all
-
# end
-
#
-
# Here's the same action, with web-service support baked in:
-
#
-
# def index
-
# @people = Person.all
-
#
-
# respond_to do |format|
-
# format.html
-
# format.xml { render xml: @people }
-
# end
-
# end
-
#
-
# What that says is, "if the client wants HTML in response to this action, just respond as we
-
# would have before, but if the client wants XML, return them the list of people in XML format."
-
# (Rails determines the desired response format from the HTTP Accept header submitted by the client.)
-
#
-
# Supposing you have an action that adds a new person, optionally creating their company
-
# (by name) if it does not already exist, without web-services, it might look like this:
-
#
-
# def create
-
# @company = Company.find_or_create_by_name(params[:company][:name])
-
# @person = @company.people.create(params[:person])
-
#
-
# redirect_to(person_list_url)
-
# end
-
#
-
# Here's the same action, with web-service support baked in:
-
#
-
# def create
-
# company = params[:person].delete(:company)
-
# @company = Company.find_or_create_by_name(company[:name])
-
# @person = @company.people.create(params[:person])
-
#
-
# respond_to do |format|
-
# format.html { redirect_to(person_list_url) }
-
# format.js
-
# format.xml { render xml: @person.to_xml(include: @company) }
-
# end
-
# end
-
#
-
# If the client wants HTML, we just redirect them back to the person list. If they want JavaScript,
-
# then it is an Ajax request and we render the JavaScript template associated with this action.
-
# Lastly, if the client wants XML, we render the created person as XML, but with a twist: we also
-
# include the person's company in the rendered XML, so you get something like this:
-
#
-
# <person>
-
# <id>...</id>
-
# ...
-
# <company>
-
# <id>...</id>
-
# <name>...</name>
-
# ...
-
# </company>
-
# </person>
-
#
-
# Note, however, the extra bit at the top of that action:
-
#
-
# company = params[:person].delete(:company)
-
# @company = Company.find_or_create_by_name(company[:name])
-
#
-
# This is because the incoming XML document (if a web-service request is in process) can only contain a
-
# single root-node. So, we have to rearrange things so that the request looks like this (url-encoded):
-
#
-
# person[name]=...&person[company][name]=...&...
-
#
-
# And, like this (xml-encoded):
-
#
-
# <person>
-
# <name>...</name>
-
# <company>
-
# <name>...</name>
-
# </company>
-
# </person>
-
#
-
# In other words, we make the request so that it operates on a single entity's person. Then, in the action,
-
# we extract the company data from the request, find or create the company, and then create the new person
-
# with the remaining data.
-
#
-
# Note that you can define your own XML parameter parser which would allow you to describe multiple entities
-
# in a single request (i.e., by wrapping them all in a single root node), but if you just go with the flow
-
# and accept Rails' defaults, life will be much easier.
-
#
-
# If you need to use a MIME type which isn't supported by default, you can register your own handlers in
-
# config/initializers/mime_types.rb as follows.
-
#
-
# Mime::Type.register "image/jpg", :jpg
-
#
-
# Respond to also allows you to specify a common block for different formats by using any:
-
#
-
# def index
-
# @people = Person.all
-
#
-
# respond_to do |format|
-
# format.html
-
# format.any(:xml, :json) { render request.format.to_sym => @people }
-
# end
-
# end
-
#
-
# In the example above, if the format is xml, it will render:
-
#
-
# render xml: @people
-
#
-
# Or if the format is json:
-
#
-
# render json: @people
-
#
-
# Since this is a common pattern, you can use the class method respond_to
-
# with the respond_with method to have the same results:
-
#
-
# class PeopleController < ApplicationController
-
# respond_to :html, :xml, :json
-
#
-
# def index
-
# @people = Person.all
-
# respond_with(@people)
-
# end
-
# end
-
#
-
# Be sure to check the documentation of +respond_with+ and
-
# <tt>ActionController::MimeResponds.respond_to</tt> for more examples.
-
1
def respond_to(*mimes, &block)
-
raise ArgumentError, "respond_to takes either types or a block, never both" if mimes.any? && block_given?
-
-
if collector = retrieve_collector_from_mimes(mimes, &block)
-
response = collector.response
-
response ? response.call : render({})
-
end
-
end
-
-
# For a given controller action, respond_with generates an appropriate
-
# response based on the mime-type requested by the client.
-
#
-
# If the method is called with just a resource, as in this example -
-
#
-
# class PeopleController < ApplicationController
-
# respond_to :html, :xml, :json
-
#
-
# def index
-
# @people = Person.all
-
# respond_with @people
-
# end
-
# end
-
#
-
# then the mime-type of the response is typically selected based on the
-
# request's Accept header and the set of available formats declared
-
# by previous calls to the controller's class method +respond_to+. Alternatively
-
# the mime-type can be selected by explicitly setting <tt>request.format</tt> in
-
# the controller.
-
#
-
# If an acceptable format is not identified, the application returns a
-
# '406 - not acceptable' status. Otherwise, the default response is to render
-
# a template named after the current action and the selected format,
-
# e.g. <tt>index.html.erb</tt>. If no template is available, the behavior
-
# depends on the selected format:
-
#
-
# * for an html response - if the request method is +get+, an exception
-
# is raised but for other requests such as +post+ the response
-
# depends on whether the resource has any validation errors (i.e.
-
# assuming that an attempt has been made to save the resource,
-
# e.g. by a +create+ action) -
-
# 1. If there are no errors, i.e. the resource
-
# was saved successfully, the response +redirect+'s to the resource
-
# i.e. its +show+ action.
-
# 2. If there are validation errors, the response
-
# renders a default action, which is <tt>:new</tt> for a
-
# +post+ request or <tt>:edit</tt> for +put+.
-
# Thus an example like this -
-
#
-
# respond_to :html, :xml
-
#
-
# def create
-
# @user = User.new(params[:user])
-
# flash[:notice] = 'User was successfully created.' if @user.save
-
# respond_with(@user)
-
# end
-
#
-
# is equivalent, in the absence of <tt>create.html.erb</tt>, to -
-
#
-
# def create
-
# @user = User.new(params[:user])
-
# respond_to do |format|
-
# if @user.save
-
# flash[:notice] = 'User was successfully created.'
-
# format.html { redirect_to(@user) }
-
# format.xml { render xml: @user }
-
# else
-
# format.html { render action: "new" }
-
# format.xml { render xml: @user }
-
# end
-
# end
-
# end
-
#
-
# * for a javascript request - if the template isn't found, an exception is
-
# raised.
-
# * for other requests - i.e. data formats such as xml, json, csv etc, if
-
# the resource passed to +respond_with+ responds to <code>to_<format></code>,
-
# the method attempts to render the resource in the requested format
-
# directly, e.g. for an xml request, the response is equivalent to calling
-
# <code>render xml: resource</code>.
-
#
-
# === Nested resources
-
#
-
# As outlined above, the +resources+ argument passed to +respond_with+
-
# can play two roles. It can be used to generate the redirect url
-
# for successful html requests (e.g. for +create+ actions when
-
# no template exists), while for formats other than html and javascript
-
# it is the object that gets rendered, by being converted directly to the
-
# required format (again assuming no template exists).
-
#
-
# For redirecting successful html requests, +respond_with+ also supports
-
# the use of nested resources, which are supplied in the same way as
-
# in <code>form_for</code> and <code>polymorphic_url</code>. For example -
-
#
-
# def create
-
# @project = Project.find(params[:project_id])
-
# @task = @project.comments.build(params[:task])
-
# flash[:notice] = 'Task was successfully created.' if @task.save
-
# respond_with(@project, @task)
-
# end
-
#
-
# This would cause +respond_with+ to redirect to <code>project_task_url</code>
-
# instead of <code>task_url</code>. For request formats other than html or
-
# javascript, if multiple resources are passed in this way, it is the last
-
# one specified that is rendered.
-
#
-
# === Customizing response behavior
-
#
-
# Like +respond_to+, +respond_with+ may also be called with a block that
-
# can be used to overwrite any of the default responses, e.g. -
-
#
-
# def create
-
# @user = User.new(params[:user])
-
# flash[:notice] = "User was successfully created." if @user.save
-
#
-
# respond_with(@user) do |format|
-
# format.html { render }
-
# end
-
# end
-
#
-
# The argument passed to the block is an ActionController::MimeResponds::Collector
-
# object which stores the responses for the formats defined within the
-
# block. Note that formats with responses defined explicitly in this way
-
# do not have to first be declared using the class method +respond_to+.
-
#
-
# Also, a hash passed to +respond_with+ immediately after the specified
-
# resource(s) is interpreted as a set of options relevant to all
-
# formats. Any option accepted by +render+ can be used, e.g.
-
# respond_with @people, status: 200
-
# However, note that these options are ignored after an unsuccessful attempt
-
# to save a resource, e.g. when automatically rendering <tt>:new</tt>
-
# after a post request.
-
#
-
# Two additional options are relevant specifically to +respond_with+ -
-
# 1. <tt>:location</tt> - overwrites the default redirect location used after
-
# a successful html +post+ request.
-
# 2. <tt>:action</tt> - overwrites the default render action used after an
-
# unsuccessful html +post+ request.
-
1
def respond_with(*resources, &block)
-
raise "In order to use respond_with, first you need to declare the formats your " <<
-
"controller responds to in the class level" if self.class.mimes_for_respond_to.empty?
-
-
if collector = retrieve_collector_from_mimes(&block)
-
options = resources.size == 1 ? {} : resources.extract_options!
-
options[:default_response] = collector.response
-
(options.delete(:responder) || self.class.responder).call(self, resources, options)
-
end
-
end
-
-
1
protected
-
-
# Collect mimes declared in the class method respond_to valid for the
-
# current action.
-
1
def collect_mimes_from_class_level #:nodoc:
-
action = action_name.to_s
-
-
self.class.mimes_for_respond_to.keys.select do |mime|
-
config = self.class.mimes_for_respond_to[mime]
-
-
if config[:except]
-
!config[:except].include?(action)
-
elsif config[:only]
-
config[:only].include?(action)
-
else
-
true
-
end
-
end
-
end
-
-
# Returns a Collector object containing the appropriate mime-type response
-
# for the current request, based on the available responses defined by a block.
-
# In typical usage this is the block passed to +respond_with+ or +respond_to+.
-
#
-
# Sends :not_acceptable to the client and returns nil if no suitable format
-
# is available.
-
1
def retrieve_collector_from_mimes(mimes=nil, &block) #:nodoc:
-
mimes ||= collect_mimes_from_class_level
-
collector = Collector.new(mimes)
-
block.call(collector) if block_given?
-
format = collector.negotiate_format(request)
-
-
if format
-
self.content_type ||= format.to_s
-
lookup_context.formats = [format.to_sym]
-
lookup_context.rendered_format = lookup_context.formats.first
-
collector
-
else
-
raise ActionController::UnknownFormat
-
end
-
end
-
-
# A container for responses available from the current controller for
-
# requests for different mime-types sent to a particular action.
-
#
-
# The public controller methods +respond_with+ and +respond_to+ may be called
-
# with a block that is used to define responses to different mime-types, e.g.
-
# for +respond_to+ :
-
#
-
# respond_to do |format|
-
# format.html
-
# format.xml { render xml: @people }
-
# end
-
#
-
# In this usage, the argument passed to the block (+format+ above) is an
-
# instance of the ActionController::MimeResponds::Collector class. This
-
# object serves as a container in which available responses can be stored by
-
# calling any of the dynamically generated, mime-type-specific methods such
-
# as +html+, +xml+ etc on the Collector. Each response is represented by a
-
# corresponding block if present.
-
#
-
# A subsequent call to #negotiate_format(request) will enable the Collector
-
# to determine which specific mime-type it should respond with for the current
-
# request, with this response then being accessible by calling #response.
-
1
class Collector
-
1
include AbstractController::Collector
-
1
attr_accessor :order, :format
-
-
1
def initialize(mimes)
-
@order, @responses = [], {}
-
mimes.each { |mime| send(mime) }
-
end
-
-
1
def any(*args, &block)
-
if args.any?
-
args.each { |type| send(type, &block) }
-
else
-
custom(Mime::ALL, &block)
-
end
-
end
-
1
alias :all :any
-
-
1
def custom(mime_type, &block)
-
mime_type = Mime::Type.lookup(mime_type.to_s) unless mime_type.is_a?(Mime::Type)
-
@order << mime_type
-
@responses[mime_type] ||= block
-
end
-
-
1
def response
-
@responses[format] || @responses[Mime::ALL]
-
end
-
-
1
def negotiate_format(request)
-
@format = request.negotiate_mime(order)
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/hash/slice'
-
1
require 'active_support/core_ext/hash/except'
-
1
require 'active_support/core_ext/module/anonymous'
-
1
require 'action_dispatch/http/mime_types'
-
-
1
module ActionController
-
# Wraps the parameters hash into a nested hash. This will allow clients to submit
-
# POST requests without having to specify any root elements.
-
#
-
# This functionality is enabled in +config/initializers/wrap_parameters.rb+
-
# and can be customized. If you are upgrading to \Rails 3.1, this file will
-
# need to be created for the functionality to be enabled.
-
#
-
# You could also turn it on per controller by setting the format array to
-
# a non-empty array:
-
#
-
# class UsersController < ApplicationController
-
# wrap_parameters format: [:json, :xml]
-
# end
-
#
-
# If you enable +ParamsWrapper+ for +:json+ format, instead of having to
-
# send JSON parameters like this:
-
#
-
# {"user": {"name": "Konata"}}
-
#
-
# You can send parameters like this:
-
#
-
# {"name": "Konata"}
-
#
-
# And it will be wrapped into a nested hash with the key name matching the
-
# controller's name. For example, if you're posting to +UsersController+,
-
# your new +params+ hash will look like this:
-
#
-
# {"name" => "Konata", "user" => {"name" => "Konata"}}
-
#
-
# You can also specify the key in which the parameters should be wrapped to,
-
# and also the list of attributes it should wrap by using either +:include+ or
-
# +:exclude+ options like this:
-
#
-
# class UsersController < ApplicationController
-
# wrap_parameters :person, include: [:username, :password]
-
# end
-
#
-
# On ActiveRecord models with no +:include+ or +:exclude+ option set,
-
# it will only wrap the parameters returned by the class method
-
# <tt>attribute_names</tt>.
-
#
-
# If you're going to pass the parameters to an +ActiveModel+ object (such as
-
# <tt>User.new(params[:user])</tt>), you might consider passing the model class to
-
# the method instead. The +ParamsWrapper+ will actually try to determine the
-
# list of attribute names from the model and only wrap those attributes:
-
#
-
# class UsersController < ApplicationController
-
# wrap_parameters Person
-
# end
-
#
-
# You still could pass +:include+ and +:exclude+ to set the list of attributes
-
# you want to wrap.
-
#
-
# By default, if you don't specify the key in which the parameters would be
-
# wrapped to, +ParamsWrapper+ will actually try to determine if there's
-
# a model related to it or not. This controller, for example:
-
#
-
# class Admin::UsersController < ApplicationController
-
# end
-
#
-
# will try to check if <tt>Admin::User</tt> or +User+ model exists, and use it to
-
# determine the wrapper key respectively. If both models don't exist,
-
# it will then fallback to use +user+ as the key.
-
1
module ParamsWrapper
-
1
extend ActiveSupport::Concern
-
-
1
EXCLUDE_PARAMETERS = %w(authenticity_token _method utf8)
-
-
1
included do
-
1
class_attribute :_wrapper_options
-
1
self._wrapper_options = { :format => [] }
-
end
-
-
1
module ClassMethods
-
# Sets the name of the wrapper key, or the model which +ParamsWrapper+
-
# would use to determine the attribute names from.
-
#
-
# ==== Examples
-
# wrap_parameters format: :xml
-
# # enables the parameter wrapper for XML format
-
#
-
# wrap_parameters :person
-
# # wraps parameters into +params[:person]+ hash
-
#
-
# wrap_parameters Person
-
# # wraps parameters by determining the wrapper key from Person class
-
# (+person+, in this case) and the list of attribute names
-
#
-
# wrap_parameters include: [:username, :title]
-
# # wraps only +:username+ and +:title+ attributes from parameters.
-
#
-
# wrap_parameters false
-
# # disables parameters wrapping for this controller altogether.
-
#
-
# ==== Options
-
# * <tt>:format</tt> - The list of formats in which the parameters wrapper
-
# will be enabled.
-
# * <tt>:include</tt> - The list of attribute names which parameters wrapper
-
# will wrap into a nested hash.
-
# * <tt>:exclude</tt> - The list of attribute names which parameters wrapper
-
# will exclude from a nested hash.
-
1
def wrap_parameters(name_or_model_or_options, options = {})
-
model = nil
-
-
case name_or_model_or_options
-
when Hash
-
options = name_or_model_or_options
-
when false
-
options = options.merge(:format => [])
-
when Symbol, String
-
options = options.merge(:name => name_or_model_or_options)
-
else
-
model = name_or_model_or_options
-
end
-
-
_set_wrapper_defaults(_wrapper_options.slice(:format).merge(options), model)
-
end
-
-
# Sets the default wrapper key or model which will be used to determine
-
# wrapper key and attribute names. Will be called automatically when the
-
# module is inherited.
-
1
def inherited(klass)
-
2
if klass._wrapper_options[:format].present?
-
klass._set_wrapper_defaults(klass._wrapper_options.slice(:format))
-
end
-
2
super
-
end
-
-
1
protected
-
-
# Determine the wrapper model from the controller's name. By convention,
-
# this could be done by trying to find the defined model that has the
-
# same singularize name as the controller. For example, +UsersController+
-
# will try to find if the +User+ model exists.
-
#
-
# This method also does namespace lookup. Foo::Bar::UsersController will
-
# try to find Foo::Bar::User, Foo::User and finally User.
-
1
def _default_wrap_model #:nodoc:
-
return nil if self.anonymous?
-
model_name = self.name.sub(/Controller$/, '').classify
-
-
begin
-
if model_klass = model_name.safe_constantize
-
model_klass
-
else
-
namespaces = model_name.split("::")
-
namespaces.delete_at(-2)
-
break if namespaces.last == model_name
-
model_name = namespaces.join("::")
-
end
-
end until model_klass
-
-
model_klass
-
end
-
-
1
def _set_wrapper_defaults(options, model=nil)
-
options = options.dup
-
-
unless options[:include] || options[:exclude]
-
model ||= _default_wrap_model
-
if model.respond_to?(:attribute_names) && model.attribute_names.present?
-
options[:include] = model.attribute_names
-
end
-
end
-
-
unless options[:name] || self.anonymous?
-
model ||= _default_wrap_model
-
options[:name] = model ? model.to_s.demodulize.underscore :
-
controller_name.singularize
-
end
-
-
options[:include] = Array(options[:include]).collect(&:to_s) if options[:include]
-
options[:exclude] = Array(options[:exclude]).collect(&:to_s) if options[:exclude]
-
options[:format] = Array(options[:format])
-
-
self._wrapper_options = options
-
end
-
end
-
-
# Performs parameters wrapping upon the request. Will be called automatically
-
# by the metal call stack.
-
1
def process_action(*args)
-
1
if _wrapper_enabled?
-
wrapped_hash = _wrap_parameters request.request_parameters
-
wrapped_keys = request.request_parameters.keys
-
wrapped_filtered_hash = _wrap_parameters request.filtered_parameters.slice(*wrapped_keys)
-
-
# This will make the wrapped hash accessible from controller and view
-
request.parameters.merge! wrapped_hash
-
request.request_parameters.merge! wrapped_hash
-
-
# This will make the wrapped hash displayed in the log file
-
request.filtered_parameters.merge! wrapped_filtered_hash
-
end
-
1
super
-
end
-
-
1
private
-
-
# Returns the wrapper key which will use to stored wrapped parameters.
-
1
def _wrapper_key
-
_wrapper_options[:name]
-
end
-
-
# Returns the list of enabled formats.
-
1
def _wrapper_formats
-
1
_wrapper_options[:format]
-
end
-
-
# Returns the list of parameters which will be selected for wrapped.
-
1
def _wrap_parameters(parameters)
-
value = if include_only = _wrapper_options[:include]
-
parameters.slice(*include_only)
-
else
-
exclude = _wrapper_options[:exclude] || []
-
parameters.except(*(exclude + EXCLUDE_PARAMETERS))
-
end
-
-
{ _wrapper_key => value }
-
end
-
-
# Checks if we should perform parameters wrapping.
-
1
def _wrapper_enabled?
-
1
ref = request.content_mime_type.try(:ref)
-
1
_wrapper_formats.include?(ref) && _wrapper_key && !request.request_parameters[_wrapper_key]
-
end
-
end
-
end
-
1
require 'action_dispatch/http/request'
-
1
require 'action_dispatch/http/response'
-
-
1
module ActionController
-
1
module RackDelegation
-
1
extend ActiveSupport::Concern
-
-
1
delegate :headers, :status=, :location=, :content_type=,
-
:status, :location, :content_type, :to => "@_response"
-
-
1
def dispatch(action, request)
-
1
set_response!(request)
-
1
super(action, request)
-
end
-
-
1
def response_body=(body)
-
1
response.body = body if response
-
1
super
-
end
-
-
1
def reset_session
-
@_request.reset_session
-
end
-
-
1
private
-
-
1
def set_response!(request)
-
1
@_response = ActionDispatch::Response.new
-
1
@_response.request = request
-
end
-
end
-
end
-
1
module ActionController
-
1
class RedirectBackError < AbstractController::Error #:nodoc:
-
1
DEFAULT_MESSAGE = 'No HTTP_REFERER was set in the request to this action, so redirect_to :back could not be called successfully. If this is a test, make sure to specify request.env["HTTP_REFERER"].'
-
-
1
def initialize(message = nil)
-
super(message || DEFAULT_MESSAGE)
-
end
-
end
-
-
1
module Redirecting
-
1
extend ActiveSupport::Concern
-
-
1
include AbstractController::Logger
-
1
include ActionController::RackDelegation
-
1
include ActionController::UrlFor
-
-
# Redirects the browser to the target specified in +options+. This parameter can take one of three forms:
-
#
-
# * <tt>Hash</tt> - The URL will be generated by calling url_for with the +options+.
-
# * <tt>Record</tt> - The URL will be generated by calling url_for with the +options+, which will reference a named URL for that record.
-
# * <tt>String</tt> starting with <tt>protocol://</tt> (like <tt>http://</tt>) or a protocol relative reference (like <tt>//</tt>) - Is passed straight through as the target for redirection.
-
# * <tt>String</tt> not containing a protocol - The current protocol and host is prepended to the string.
-
# * <tt>Proc</tt> - A block that will be executed in the controller's context. Should return any option accepted by +redirect_to+.
-
# * <tt>:back</tt> - Back to the page that issued the request. Useful for forms that are triggered from multiple places.
-
# Short-hand for <tt>redirect_to(request.env["HTTP_REFERER"])</tt>
-
#
-
# redirect_to action: "show", id: 5
-
# redirect_to post
-
# redirect_to "http://www.rubyonrails.org"
-
# redirect_to "/images/screenshot.jpg"
-
# redirect_to articles_url
-
# redirect_to :back
-
# redirect_to proc { edit_post_url(@post) }
-
#
-
# The redirection happens as a "302 Moved" header unless otherwise specified.
-
#
-
# redirect_to post_url(@post), status: :found
-
# redirect_to action: 'atom', status: :moved_permanently
-
# redirect_to post_url(@post), status: 301
-
# redirect_to action: 'atom', status: 302
-
#
-
# The status code can either be a standard {HTTP Status code}[http://www.iana.org/assignments/http-status-codes] as an
-
# integer, or a symbol representing the downcased, underscored and symbolized description.
-
# Note that the status code must be a 3xx HTTP code, or redirection will not occur.
-
#
-
# If you are using XHR requests other than GET or POST and redirecting after the
-
# request then some browsers will follow the redirect using the original request
-
# method. This may lead to undesirable behavior such as a double DELETE. To work
-
# around this you can return a <tt>303 See Other</tt> status code which will be
-
# followed using a GET request.
-
#
-
# redirect_to posts_url, status: :see_other
-
# redirect_to action: 'index', status: 303
-
#
-
# It is also possible to assign a flash message as part of the redirection. There are two special accessors for the commonly used flash names
-
# +alert+ and +notice+ as well as a general purpose +flash+ bucket.
-
#
-
# redirect_to post_url(@post), alert: "Watch it, mister!"
-
# redirect_to post_url(@post), status: :found, notice: "Pay attention to the road"
-
# redirect_to post_url(@post), status: 301, flash: { updated_post_id: @post.id }
-
# redirect_to { action: 'atom' }, alert: "Something serious happened"
-
#
-
# When using <tt>redirect_to :back</tt>, if there is no referrer, ActionController::RedirectBackError will be raised. You may specify some fallback
-
# behavior for this case by rescuing ActionController::RedirectBackError.
-
1
def redirect_to(options = {}, response_status = {}) #:doc:
-
raise ActionControllerError.new("Cannot redirect to nil!") unless options
-
raise AbstractController::DoubleRenderError if response_body
-
logger.debug { "Redirected by #{caller(1).first rescue "unknown"}" } if logger
-
-
self.status = _extract_redirect_to_status(options, response_status)
-
self.location = _compute_redirect_to_location(options)
-
self.response_body = "<html><body>You are being <a href=\"#{ERB::Util.h(location)}\">redirected</a>.</body></html>"
-
end
-
-
1
private
-
1
def _extract_redirect_to_status(options, response_status)
-
status = if options.is_a?(Hash) && options.key?(:status)
-
Rack::Utils.status_code(options.delete(:status))
-
elsif response_status.key?(:status)
-
Rack::Utils.status_code(response_status[:status])
-
else
-
302
-
end
-
end
-
-
1
def _compute_redirect_to_location(options)
-
case options
-
# The scheme name consist of a letter followed by any combination of
-
# letters, digits, and the plus ("+"), period ("."), or hyphen ("-")
-
# characters; and is terminated by a colon (":").
-
# The protocol relative scheme starts with a double slash "//"
-
when %r{^(\w[\w+.-]*:|//).*}
-
options
-
when String
-
request.protocol + request.host_with_port + options
-
when :back
-
raise RedirectBackError unless refer = request.headers["Referer"]
-
refer
-
when Proc
-
_compute_redirect_to_location options.call
-
else
-
url_for(options)
-
end.delete("\0\r\n")
-
end
-
end
-
end
-
1
require 'set'
-
-
1
module ActionController
-
# See <tt>Renderers.add</tt>
-
1
def self.add_renderer(key, &block)
-
Renderers.add(key, &block)
-
end
-
-
1
module Renderers
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
1
class_attribute :_renderers
-
1
self._renderers = Set.new.freeze
-
end
-
-
1
module ClassMethods
-
1
def use_renderers(*args)
-
renderers = _renderers + args
-
self._renderers = renderers.freeze
-
end
-
1
alias use_renderer use_renderers
-
end
-
-
1
def render_to_body(options)
-
1
_handle_render_options(options) || super
-
end
-
-
1
def _handle_render_options(options)
-
1
_renderers.each do |name|
-
3
if options.key?(name)
-
_process_options(options)
-
return send("_render_option_#{name}", options.delete(name), options)
-
end
-
end
-
nil
-
end
-
-
# Hash of available renderers, mapping a renderer name to its proc.
-
# Default keys are :json, :js, :xml.
-
1
RENDERERS = Set.new
-
-
# Adds a new renderer to call within controller actions.
-
# A renderer is invoked by passing its name as an option to
-
# <tt>AbstractController::Rendering#render</tt>. To create a renderer
-
# pass it a name and a block. The block takes two arguments, the first
-
# is the value paired with its key and the second is the remaining
-
# hash of options passed to +render+.
-
#
-
# Create a csv renderer:
-
#
-
# ActionController::Renderers.add :csv do |obj, options|
-
# filename = options[:filename] || 'data'
-
# str = obj.respond_to?(:to_csv) ? obj.to_csv : obj.to_s
-
# send_data str, type: Mime::CSV,
-
# disposition: "attachment; filename=#{filename}.csv"
-
# end
-
#
-
# Note that we used Mime::CSV for the csv mime type as it comes with Rails.
-
# For a custom renderer, you'll need to register a mime type with
-
# <tt>Mime::Type.register</tt>.
-
#
-
# To use the csv renderer in a controller action:
-
#
-
# def show
-
# @csvable = Csvable.find(params[:id])
-
# respond_to do |format|
-
# format.html
-
# format.csv { render csv: @csvable, filename: @csvable.name }
-
# }
-
# end
-
# To use renderers and their mime types in more concise ways, see
-
# <tt>ActionController::MimeResponds::ClassMethods.respond_to</tt> and
-
# <tt>ActionController::MimeResponds#respond_with</tt>
-
1
def self.add(key, &block)
-
3
define_method("_render_option_#{key}", &block)
-
3
RENDERERS << key.to_sym
-
end
-
-
1
module All
-
1
extend ActiveSupport::Concern
-
1
include Renderers
-
-
1
included do
-
1
self._renderers = RENDERERS
-
end
-
end
-
-
1
add :json do |json, options|
-
json = json.to_json(options) unless json.kind_of?(String)
-
-
if options[:callback].present?
-
self.content_type ||= Mime::JS
-
"#{options[:callback]}(#{json})"
-
else
-
self.content_type ||= Mime::JSON
-
json
-
end
-
end
-
-
1
add :js do |js, options|
-
self.content_type ||= Mime::JS
-
js.respond_to?(:to_js) ? js.to_js(options) : js
-
end
-
-
1
add :xml do |xml, options|
-
self.content_type ||= Mime::XML
-
xml.respond_to?(:to_xml) ? xml.to_xml(options) : xml
-
end
-
end
-
end
-
1
module ActionController
-
1
module Rendering
-
1
extend ActiveSupport::Concern
-
-
1
include AbstractController::Rendering
-
-
# Before processing, set the request formats in current controller formats.
-
1
def process_action(*) #:nodoc:
-
2
self.formats = request.formats.map { |x| x.ref }
-
1
super
-
end
-
-
# Check for double render errors and set the content_type after rendering.
-
1
def render(*args) #:nodoc:
-
1
raise ::AbstractController::DoubleRenderError if response_body
-
1
super
-
1
self.content_type ||= Mime[lookup_context.rendered_format].to_s
-
1
response_body
-
end
-
-
# Overwrite render_to_string because body can now be set to a rack body.
-
1
def render_to_string(*)
-
if self.response_body = super
-
string = ""
-
response_body.each { |r| string << r }
-
string
-
end
-
ensure
-
self.response_body = nil
-
end
-
-
1
def render_to_body(*)
-
1
super || " "
-
end
-
-
1
private
-
-
# Normalize arguments by catching blocks and setting them on :update.
-
1
def _normalize_args(action=nil, options={}, &blk) #:nodoc:
-
1
options = super
-
1
options[:update] = blk if block_given?
-
1
options
-
end
-
-
# Normalize both text and status options.
-
1
def _normalize_options(options) #:nodoc:
-
1
if options.key?(:text) && options[:text].respond_to?(:to_text)
-
options[:text] = options[:text].to_text
-
end
-
-
1
if options.delete(:nothing) || (options.key?(:text) && options[:text].nil?)
-
options[:text] = " "
-
end
-
-
1
if options[:status]
-
options[:status] = Rack::Utils.status_code(options[:status])
-
end
-
-
1
super
-
end
-
-
# Process controller specific options, as status, content-type and location.
-
1
def _process_options(options) #:nodoc:
-
1
status, content_type, location = options.values_at(:status, :content_type, :location)
-
-
1
self.status = status if status
-
1
self.content_type = content_type if content_type
-
1
self.headers["Location"] = url_for(location) if location
-
-
1
super
-
end
-
end
-
end
-
1
require 'rack/session/abstract/id'
-
1
require 'action_controller/metal/exceptions'
-
-
1
module ActionController #:nodoc:
-
1
class InvalidAuthenticityToken < ActionControllerError #:nodoc:
-
end
-
-
# Controller actions are protected from Cross-Site Request Forgery (CSRF) attacks
-
# by including a token in the rendered html for your application. This token is
-
# stored as a random string in the session, to which an attacker does not have
-
# access. When a request reaches your application, \Rails verifies the received
-
# token with the token in the session. Only HTML and JavaScript requests are checked,
-
# so this will not protect your XML API (presumably you'll have a different
-
# authentication scheme there anyway). Also, GET requests are not protected as these
-
# should be idempotent.
-
#
-
# It's important to remember that XML or JSON requests are also affected and if
-
# you're building an API you'll need something like:
-
#
-
# class ApplicationController < ActionController::Base
-
# protect_from_forgery
-
# skip_before_filter :verify_authenticity_token, if: :json_request?
-
#
-
# protected
-
#
-
# def json_request?
-
# request.format.json?
-
# end
-
# end
-
#
-
# CSRF protection is turned on with the <tt>protect_from_forgery</tt> method,
-
# which checks the token and resets the session if it doesn't match what was expected.
-
# A call to this method is generated for new \Rails applications by default.
-
#
-
# The token parameter is named <tt>authenticity_token</tt> by default. The name and
-
# value of this token must be added to every layout that renders forms by including
-
# <tt>csrf_meta_tags</tt> in the html +head+.
-
#
-
# Learn more about CSRF attacks and securing your application in the
-
# {Ruby on Rails Security Guide}[http://guides.rubyonrails.org/security.html].
-
1
module RequestForgeryProtection
-
1
extend ActiveSupport::Concern
-
-
1
include AbstractController::Helpers
-
1
include AbstractController::Callbacks
-
-
1
included do
-
# Sets the token parameter name for RequestForgery. Calling +protect_from_forgery+
-
# sets it to <tt>:authenticity_token</tt> by default.
-
1
config_accessor :request_forgery_protection_token
-
1
self.request_forgery_protection_token ||= :authenticity_token
-
-
# Controls whether request forgery protection is turned on or not. Turned off by default only in test mode.
-
1
config_accessor :allow_forgery_protection
-
1
self.allow_forgery_protection = true if allow_forgery_protection.nil?
-
-
1
helper_method :form_authenticity_token
-
1
helper_method :protect_against_forgery?
-
end
-
-
1
module ClassMethods
-
# Turn on request forgery protection. Bear in mind that only non-GET, HTML/JavaScript requests are checked.
-
#
-
# class FooController < ApplicationController
-
# protect_from_forgery except: :index
-
#
-
# You can disable csrf protection on controller-by-controller basis:
-
#
-
# skip_before_filter :verify_authenticity_token
-
#
-
# It can also be disabled for specific controller actions:
-
#
-
# skip_before_filter :verify_authenticity_token, except: [:create]
-
#
-
# Valid Options:
-
#
-
# * <tt>:only/:except</tt> - Passed to the <tt>before_filter</tt> call. Set which actions are verified.
-
# * <tt>:with</tt> - Set the method to handle unverified request.
-
#
-
# Valid unverified request handling methods are:
-
# * <tt>:exception</tt> - Raises ActionController::InvalidAuthenticityToken exception.
-
# * <tt>:reset_session</tt> - Resets the session.
-
# * <tt>:null_session</tt> - Provides an empty session during request but doesn't reset it completely. Used as default if <tt>:with</tt> option is not specified.
-
1
def protect_from_forgery(options = {})
-
include protection_method_module(options[:with] || :null_session)
-
self.request_forgery_protection_token ||= :authenticity_token
-
prepend_before_filter :verify_authenticity_token, options
-
end
-
-
1
private
-
-
1
def protection_method_module(name)
-
ActionController::RequestForgeryProtection::ProtectionMethods.const_get(name.to_s.classify)
-
rescue NameError
-
raise ArgumentError, 'Invalid request forgery protection method, use :null_session, :exception, or :reset_session'
-
end
-
end
-
-
1
module ProtectionMethods
-
1
module NullSession
-
1
protected
-
-
# This is the method that defines the application behavior when a request is found to be unverified.
-
1
def handle_unverified_request
-
request.session = NullSessionHash.new
-
request.env['action_dispatch.request.flash_hash'] = nil
-
request.env['rack.session.options'] = { skip: true }
-
request.env['action_dispatch.cookies'] = NullCookieJar.build(request)
-
end
-
-
1
class NullSessionHash < Rack::Session::Abstract::SessionHash #:nodoc:
-
1
def initialize
-
super(nil, nil)
-
@loaded = true
-
end
-
-
1
def exists?
-
true
-
end
-
end
-
-
1
class NullCookieJar < ActionDispatch::Cookies::CookieJar #:nodoc:
-
1
def self.build(request)
-
secret = request.env[ActionDispatch::Cookies::TOKEN_KEY]
-
host = request.host
-
secure = request.ssl?
-
-
new(secret, host, secure)
-
end
-
-
1
def write(*)
-
# nothing
-
end
-
end
-
end
-
-
1
module ResetSession
-
1
protected
-
-
1
def handle_unverified_request
-
reset_session
-
end
-
end
-
-
1
module Exception
-
1
protected
-
-
1
def handle_unverified_request
-
raise ActionController::InvalidAuthenticityToken
-
end
-
end
-
end
-
-
1
protected
-
# The actual before_filter that is used. Modify this to change how you handle unverified requests.
-
1
def verify_authenticity_token
-
unless verified_request?
-
logger.warn "Can't verify CSRF token authenticity" if logger
-
handle_unverified_request
-
end
-
end
-
-
# Returns true or false if a request is verified. Checks:
-
#
-
# * is it a GET request? Gets should be safe and idempotent
-
# * Does the form_authenticity_token match the given token value from the params?
-
# * Does the X-CSRF-Token header match the form_authenticity_token
-
1
def verified_request?
-
!protect_against_forgery? || request.get? ||
-
form_authenticity_token == params[request_forgery_protection_token] ||
-
form_authenticity_token == request.headers['X-CSRF-Token']
-
end
-
-
# Sets the token value for the current session.
-
1
def form_authenticity_token
-
session[:_csrf_token] ||= SecureRandom.base64(32)
-
end
-
-
# The form's authenticity parameter. Override to provide your own.
-
1
def form_authenticity_param
-
params[request_forgery_protection_token]
-
end
-
-
1
def protect_against_forgery?
-
allow_forgery_protection
-
end
-
end
-
end
-
1
module ActionController #:nodoc:
-
# This module is responsible to provide `rescue_from` helpers
-
# to controllers and configure when detailed exceptions must be
-
# shown.
-
1
module Rescue
-
1
extend ActiveSupport::Concern
-
1
include ActiveSupport::Rescuable
-
-
1
def rescue_with_handler(exception)
-
if (exception.respond_to?(:original_exception) &&
-
(orig_exception = exception.original_exception) &&
-
handler_for_rescue(orig_exception))
-
exception = orig_exception
-
end
-
super(exception)
-
end
-
-
# Override this method if you want to customize when detailed
-
# exceptions must be shown. This method is only called when
-
# consider_all_requests_local is false. By default, it returns
-
# false, but someone may set it to `request.local?` so local
-
# requests in production still shows the detailed exception pages.
-
1
def show_detailed_exceptions?
-
false
-
end
-
-
1
private
-
1
def process_action(*args)
-
1
super
-
rescue Exception => exception
-
request.env['action_dispatch.show_detailed_exceptions'] ||= show_detailed_exceptions?
-
rescue_with_handler(exception) || raise(exception)
-
end
-
end
-
end
-
1
require 'active_support/json'
-
-
1
module ActionController #:nodoc:
-
# Responsible for exposing a resource to different mime requests,
-
# usually depending on the HTTP verb. The responder is triggered when
-
# <code>respond_with</code> is called. The simplest case to study is a GET request:
-
#
-
# class PeopleController < ApplicationController
-
# respond_to :html, :xml, :json
-
#
-
# def index
-
# @people = Person.all
-
# respond_with(@people)
-
# end
-
# end
-
#
-
# When a request comes in, for example for an XML response, three steps happen:
-
#
-
# 1) the responder searches for a template at people/index.xml;
-
#
-
# 2) if the template is not available, it will invoke <code>#to_xml</code> on the given resource;
-
#
-
# 3) if the responder does not <code>respond_to :to_xml</code>, call <code>#to_format</code> on it.
-
#
-
# === Builtin HTTP verb semantics
-
#
-
# The default \Rails responder holds semantics for each HTTP verb. Depending on the
-
# content type, verb and the resource status, it will behave differently.
-
#
-
# Using \Rails default responder, a POST request for creating an object could
-
# be written as:
-
#
-
# def create
-
# @user = User.new(params[:user])
-
# flash[:notice] = 'User was successfully created.' if @user.save
-
# respond_with(@user)
-
# end
-
#
-
# Which is exactly the same as:
-
#
-
# def create
-
# @user = User.new(params[:user])
-
#
-
# respond_to do |format|
-
# if @user.save
-
# flash[:notice] = 'User was successfully created.'
-
# format.html { redirect_to(@user) }
-
# format.xml { render xml: @user, status: :created, location: @user }
-
# else
-
# format.html { render action: "new" }
-
# format.xml { render xml: @user.errors, status: :unprocessable_entity }
-
# end
-
# end
-
# end
-
#
-
# The same happens for PATCH/PUT and DELETE requests.
-
#
-
# === Nested resources
-
#
-
# You can supply nested resources as you do in <code>form_for</code> and <code>polymorphic_url</code>.
-
# Consider the project has many tasks example. The create action for
-
# TasksController would be like:
-
#
-
# def create
-
# @project = Project.find(params[:project_id])
-
# @task = @project.tasks.build(params[:task])
-
# flash[:notice] = 'Task was successfully created.' if @task.save
-
# respond_with(@project, @task)
-
# end
-
#
-
# Giving several resources ensures that the responder will redirect to
-
# <code>project_task_url</code> instead of <code>task_url</code>.
-
#
-
# Namespaced and singleton resources require a symbol to be given, as in
-
# polymorphic urls. If a project has one manager which has many tasks, it
-
# should be invoked as:
-
#
-
# respond_with(@project, :manager, @task)
-
#
-
# Note that if you give an array, it will be treated as a collection,
-
# so the following is not equivalent:
-
#
-
# respond_with [@project, :manager, @task]
-
#
-
# === Custom options
-
#
-
# <code>respond_with</code> also allows you to pass options that are forwarded
-
# to the underlying render call. Those options are only applied for success
-
# scenarios. For instance, you can do the following in the create method above:
-
#
-
# def create
-
# @project = Project.find(params[:project_id])
-
# @task = @project.tasks.build(params[:task])
-
# flash[:notice] = 'Task was successfully created.' if @task.save
-
# respond_with(@project, @task, status: 201)
-
# end
-
#
-
# This will return status 201 if the task was saved successfully. If not,
-
# it will simply ignore the given options and return status 422 and the
-
# resource errors. To customize the failure scenario, you can pass a
-
# a block to <code>respond_with</code>:
-
#
-
# def create
-
# @project = Project.find(params[:project_id])
-
# @task = @project.tasks.build(params[:task])
-
# respond_with(@project, @task, status: 201) do |format|
-
# if @task.save
-
# flash[:notice] = 'Task was successfully created.'
-
# else
-
# format.html { render "some_special_template" }
-
# end
-
# end
-
# end
-
#
-
# Using <code>respond_with</code> with a block follows the same syntax as <code>respond_to</code>.
-
1
class Responder
-
1
attr_reader :controller, :request, :format, :resource, :resources, :options
-
-
1
DEFAULT_ACTIONS_FOR_VERBS = {
-
:post => :new,
-
:patch => :edit,
-
:put => :edit
-
}
-
-
1
def initialize(controller, resources, options={})
-
@controller = controller
-
@request = @controller.request
-
@format = @controller.formats.first
-
@resource = resources.last
-
@resources = resources
-
@options = options
-
@action = options.delete(:action)
-
@default_response = options.delete(:default_response)
-
end
-
-
1
delegate :head, :render, :redirect_to, :to => :controller
-
1
delegate :get?, :post?, :patch?, :put?, :delete?, :to => :request
-
-
# Undefine :to_json and :to_yaml since it's defined on Object
-
1
undef_method(:to_json) if method_defined?(:to_json)
-
1
undef_method(:to_yaml) if method_defined?(:to_yaml)
-
-
# Initializes a new responder an invoke the proper format. If the format is
-
# not defined, call to_format.
-
#
-
1
def self.call(*args)
-
new(*args).respond
-
end
-
-
# Main entry point for responder responsible to dispatch to the proper format.
-
#
-
1
def respond
-
method = "to_#{format}"
-
respond_to?(method) ? send(method) : to_format
-
end
-
-
# HTML format does not render the resource, it always attempt to render a
-
# template.
-
#
-
1
def to_html
-
default_render
-
rescue ActionView::MissingTemplate => e
-
navigation_behavior(e)
-
end
-
-
# to_js simply tries to render a template. If no template is found, raises the error.
-
1
def to_js
-
default_render
-
end
-
-
# All other formats follow the procedure below. First we try to render a
-
# template, if the template is not available, we verify if the resource
-
# responds to :to_format and display it.
-
#
-
1
def to_format
-
if get? || !has_errors? || response_overridden?
-
default_render
-
else
-
display_errors
-
end
-
rescue ActionView::MissingTemplate => e
-
api_behavior(e)
-
end
-
-
1
protected
-
-
# This is the common behavior for formats associated with browsing, like :html, :iphone and so forth.
-
1
def navigation_behavior(error)
-
if get?
-
raise error
-
elsif has_errors? && default_action
-
render :action => default_action
-
else
-
redirect_to navigation_location
-
end
-
end
-
-
# This is the common behavior for formats associated with APIs, such as :xml and :json.
-
1
def api_behavior(error)
-
raise error unless resourceful?
-
-
if get?
-
display resource
-
elsif post?
-
display resource, :status => :created, :location => api_location
-
else
-
head :no_content
-
end
-
end
-
-
# Checks whether the resource responds to the current format or not.
-
#
-
1
def resourceful?
-
resource.respond_to?("to_#{format}")
-
end
-
-
# Returns the resource location by retrieving it from the options or
-
# returning the resources array.
-
#
-
1
def resource_location
-
options[:location] || resources
-
end
-
1
alias :navigation_location :resource_location
-
1
alias :api_location :resource_location
-
-
# If a response block was given, use it, otherwise call render on
-
# controller.
-
#
-
1
def default_render
-
if @default_response
-
@default_response.call(options)
-
else
-
controller.default_render(options)
-
end
-
end
-
-
# Display is just a shortcut to render a resource with the current format.
-
#
-
# display @user, status: :ok
-
#
-
# For XML requests it's equivalent to:
-
#
-
# render xml: @user, status: :ok
-
#
-
# Options sent by the user are also used:
-
#
-
# respond_with(@user, status: :created)
-
# display(@user, status: :ok)
-
#
-
# Results in:
-
#
-
# render xml: @user, status: :created
-
#
-
1
def display(resource, given_options={})
-
controller.render given_options.merge!(options).merge!(format => resource)
-
end
-
-
1
def display_errors
-
controller.render format => resource_errors, :status => :unprocessable_entity
-
end
-
-
# Check whether the resource has errors.
-
#
-
1
def has_errors?
-
resource.respond_to?(:errors) && !resource.errors.empty?
-
end
-
-
# By default, render the <code>:edit</code> action for HTML requests with errors, unless
-
# the verb was POST.
-
#
-
1
def default_action
-
@action ||= DEFAULT_ACTIONS_FOR_VERBS[request.request_method_symbol]
-
end
-
-
1
def resource_errors
-
respond_to?("#{format}_resource_errors", true) ? send("#{format}_resource_errors") : resource.errors
-
end
-
-
1
def json_resource_errors
-
{:errors => resource.errors}
-
end
-
-
1
def response_overridden?
-
@default_response.present?
-
end
-
end
-
end
-
1
require 'rack/chunked'
-
-
1
module ActionController #:nodoc:
-
# Allows views to be streamed back to the client as they are rendered.
-
#
-
# The default way Rails renders views is by first rendering the template
-
# and then the layout. The response is sent to the client after the whole
-
# template is rendered, all queries are made, and the layout is processed.
-
#
-
# Streaming inverts the rendering flow by rendering the layout first and
-
# streaming each part of the layout as they are processed. This allows the
-
# header of the HTML (which is usually in the layout) to be streamed back
-
# to client very quickly, allowing JavaScripts and stylesheets to be loaded
-
# earlier than usual.
-
#
-
# This approach was introduced in Rails 3.1 and is still improving. Several
-
# Rack middlewares may not work and you need to be careful when streaming.
-
# Those points are going to be addressed soon.
-
#
-
# In order to use streaming, you will need to use a Ruby version that
-
# supports fibers (fibers are supported since version 1.9.2 of the main
-
# Ruby implementation).
-
#
-
# == Examples
-
#
-
# Streaming can be added to a given template easily, all you need to do is
-
# to pass the :stream option.
-
#
-
# class PostsController
-
# def index
-
# @posts = Post.scoped
-
# render stream: true
-
# end
-
# end
-
#
-
# == When to use streaming
-
#
-
# Streaming may be considered to be overkill for lightweight actions like
-
# +new+ or +edit+. The real benefit of streaming is on expensive actions
-
# that, for example, do a lot of queries on the database.
-
#
-
# In such actions, you want to delay queries execution as much as you can.
-
# For example, imagine the following +dashboard+ action:
-
#
-
# def dashboard
-
# @posts = Post.all
-
# @pages = Page.all
-
# @articles = Article.all
-
# end
-
#
-
# Most of the queries here are happening in the controller. In order to benefit
-
# from streaming you would want to rewrite it as:
-
#
-
# def dashboard
-
# # Allow lazy execution of the queries
-
# @posts = Post.scoped
-
# @pages = Page.scoped
-
# @articles = Article.scoped
-
# render stream: true
-
# end
-
#
-
# Notice that :stream only works with templates. Rendering :json
-
# or :xml with :stream won't work.
-
#
-
# == Communication between layout and template
-
#
-
# When streaming, rendering happens top-down instead of inside-out.
-
# Rails starts with the layout, and the template is rendered later,
-
# when its +yield+ is reached.
-
#
-
# This means that, if your application currently relies on instance
-
# variables set in the template to be used in the layout, they won't
-
# work once you move to streaming. The proper way to communicate
-
# between layout and template, regardless of whether you use streaming
-
# or not, is by using +content_for+, +provide+ and +yield+.
-
#
-
# Take a simple example where the layout expects the template to tell
-
# which title to use:
-
#
-
# <html>
-
# <head><title><%= yield :title %></title></head>
-
# <body><%= yield %></body>
-
# </html>
-
#
-
# You would use +content_for+ in your template to specify the title:
-
#
-
# <%= content_for :title, "Main" %>
-
# Hello
-
#
-
# And the final result would be:
-
#
-
# <html>
-
# <head><title>Main</title></head>
-
# <body>Hello</body>
-
# </html>
-
#
-
# However, if +content_for+ is called several times, the final result
-
# would have all calls concatenated. For instance, if we have the following
-
# template:
-
#
-
# <%= content_for :title, "Main" %>
-
# Hello
-
# <%= content_for :title, " page" %>
-
#
-
# The final result would be:
-
#
-
# <html>
-
# <head><title>Main page</title></head>
-
# <body>Hello</body>
-
# </html>
-
#
-
# This means that, if you have <code>yield :title</code> in your layout
-
# and you want to use streaming, you would have to render the whole template
-
# (and eventually trigger all queries) before streaming the title and all
-
# assets, which kills the purpose of streaming. For this reason Rails 3.1
-
# introduces a new helper called +provide+ that does the same as +content_for+
-
# but tells the layout to stop searching for other entries and continue rendering.
-
#
-
# For instance, the template above using +provide+ would be:
-
#
-
# <%= provide :title, "Main" %>
-
# Hello
-
# <%= content_for :title, " page" %>
-
#
-
# Giving:
-
#
-
# <html>
-
# <head><title>Main</title></head>
-
# <body>Hello</body>
-
# </html>
-
#
-
# That said, when streaming, you need to properly check your templates
-
# and choose when to use +provide+ and +content_for+.
-
#
-
# == Headers, cookies, session and flash
-
#
-
# When streaming, the HTTP headers are sent to the client right before
-
# it renders the first line. This means that, modifying headers, cookies,
-
# session or flash after the template starts rendering will not propagate
-
# to the client.
-
#
-
# == Middlewares
-
#
-
# Middlewares that need to manipulate the body won't work with streaming.
-
# You should disable those middlewares whenever streaming in development
-
# or production. For instance, <tt>Rack::Bug</tt> won't work when streaming as it
-
# needs to inject contents in the HTML body.
-
#
-
# Also <tt>Rack::Cache</tt> won't work with streaming as it does not support
-
# streaming bodies yet. Whenever streaming Cache-Control is automatically
-
# set to "no-cache".
-
#
-
# == Errors
-
#
-
# When it comes to streaming, exceptions get a bit more complicated. This
-
# happens because part of the template was already rendered and streamed to
-
# the client, making it impossible to render a whole exception page.
-
#
-
# Currently, when an exception happens in development or production, Rails
-
# will automatically stream to the client:
-
#
-
# "><script>window.location = "/500.html"</script></html>
-
#
-
# The first two characters (">) are required in case the exception happens
-
# while rendering attributes for a given tag. You can check the real cause
-
# for the exception in your logger.
-
#
-
# == Web server support
-
#
-
# Not all web servers support streaming out-of-the-box. You need to check
-
# the instructions for each of them.
-
#
-
# ==== Unicorn
-
#
-
# Unicorn supports streaming but it needs to be configured. For this, you
-
# need to create a config file as follow:
-
#
-
# # unicorn.config.rb
-
# listen 3000, tcp_nopush: false
-
#
-
# And use it on initialization:
-
#
-
# unicorn_rails --config-file unicorn.config.rb
-
#
-
# You may also want to configure other parameters like <tt>:tcp_nodelay</tt>.
-
# Please check its documentation for more information: http://unicorn.bogomips.org/Unicorn/Configurator.html#method-i-listen
-
#
-
# If you are using Unicorn with Nginx, you may need to tweak Nginx.
-
# Streaming should work out of the box on Rainbows.
-
#
-
# ==== Passenger
-
#
-
# To be described.
-
#
-
1
module Streaming
-
1
extend ActiveSupport::Concern
-
-
1
include AbstractController::Rendering
-
-
1
protected
-
-
# Set proper cache control and transfer encoding when streaming
-
1
def _process_options(options) #:nodoc:
-
1
super
-
1
if options[:stream]
-
if env["HTTP_VERSION"] == "HTTP/1.0"
-
options.delete(:stream)
-
else
-
headers["Cache-Control"] ||= "no-cache"
-
headers["Transfer-Encoding"] = "chunked"
-
headers.delete("Content-Length")
-
end
-
end
-
end
-
-
# Call render_body if we are streaming instead of usual +render+.
-
1
def _render_template(options) #:nodoc:
-
1
if options.delete(:stream)
-
Rack::Chunked::Body.new view_renderer.render_body(view_context, options)
-
else
-
1
super
-
end
-
end
-
end
-
end
-
1
require 'active_support/concern'
-
1
require 'active_support/core_ext/hash/indifferent_access'
-
1
require 'active_support/rescuable'
-
-
1
module ActionController
-
# Raised when a required parameter is missing.
-
#
-
# params = ActionController::Parameters.new(a: {})
-
# params.fetch(:b)
-
# # => ActionController::ParameterMissing: param not found: b
-
# params.require(:a)
-
# # => ActionController::ParameterMissing: param not found: a
-
1
class ParameterMissing < KeyError
-
1
attr_reader :param # :nodoc:
-
-
1
def initialize(param) # :nodoc:
-
@param = param
-
super("param not found: #{param}")
-
end
-
end
-
-
# == Action Controller \Parameters
-
#
-
# Allows to choose which attributes should be whitelisted for mass updating
-
# and thus prevent accidentally exposing that which shouldn’t be exposed.
-
# Provides two methods for this purpose: #require and #permit. The former is
-
# used to mark parameters as required. The latter is used to set the parameter
-
# as permitted and limit which attributes should be allowed for mass updating.
-
#
-
# params = ActionController::Parameters.new({
-
# person: {
-
# name: 'Francesco',
-
# age: 22,
-
# role: 'admin'
-
# }
-
# })
-
#
-
# permitted = params.require(:person).permit(:name, :age)
-
# permitted # => {"name"=>"Francesco", "age"=>22}
-
# permitted.class # => ActionController::Parameters
-
# permitted.permitted? # => true
-
#
-
# Person.first.update_attributes!(permitted)
-
# # => #<Person id: 1, name: "Francesco", age: 22, role: "user">
-
#
-
# It provides a +permit_all_parameters+ option that controls the top-level
-
# behaviour of new instances. If it's +true+, all the parameters will be
-
# permitted by default. The default value for +permit_all_parameters+
-
# option is +false+.
-
#
-
# params = ActionController::Parameters.new
-
# params.permitted? # => false
-
#
-
# ActionController::Parameters.permit_all_parameters = true
-
#
-
# params = ActionController::Parameters.new
-
# params.permitted? # => true
-
#
-
# <tt>ActionController::Parameters</tt> is inherited from
-
# <tt>ActiveSupport::HashWithIndifferentAccess</tt>, this means
-
# that you can fetch values using either <tt>:key</tt> or <tt>"key"</tt>.
-
#
-
# params = ActionController::Parameters.new(key: 'value')
-
# params[:key] # => "value"
-
# params["key"] # => "value"
-
1
class Parameters < ActiveSupport::HashWithIndifferentAccess
-
1
cattr_accessor :permit_all_parameters, instance_accessor: false
-
-
# Returns a new instance of <tt>ActionController::Parameters</tt>.
-
# Also, sets the +permitted+ attribute to the default value of
-
# <tt>ActionController::Parameters.permit_all_parameters</tt>.
-
#
-
# class Person
-
# include ActiveRecord::Base
-
# end
-
#
-
# params = ActionController::Parameters.new(name: 'Francesco')
-
# params.permitted? # => false
-
# Person.new(params) # => ActiveModel::ForbiddenAttributesError
-
#
-
# ActionController::Parameters.permit_all_parameters = true
-
#
-
# params = ActionController::Parameters.new(name: 'Francesco')
-
# params.permitted? # => true
-
# Person.new(params) # => #<Person id: nil, name: "Francesco">
-
1
def initialize(attributes = nil)
-
super(attributes)
-
@permitted = self.class.permit_all_parameters
-
end
-
-
# Returns +true+ if the parameter is permitted, +false+ otherwise.
-
#
-
# params = ActionController::Parameters.new
-
# params.permitted? # => false
-
# params.permit!
-
# params.permitted? # => true
-
1
def permitted?
-
@permitted
-
end
-
-
# Sets the +permitted+ attribute to +true+. This can be used to pass
-
# mass assignment. Returns +self+.
-
#
-
# class Person < ActiveRecord::Base
-
# end
-
#
-
# params = ActionController::Parameters.new(name: 'Francesco')
-
# params.permitted? # => false
-
# Person.new(params) # => ActiveModel::ForbiddenAttributesError
-
# params.permit!
-
# params.permitted? # => true
-
# Person.new(params) # => #<Person id: nil, name: "Francesco">
-
1
def permit!
-
each_pair do |key, value|
-
convert_hashes_to_parameters(key, value)
-
self[key].permit! if self[key].respond_to? :permit!
-
end
-
-
@permitted = true
-
self
-
end
-
-
# Ensures that a parameter is present. If it's present, returns
-
# the parameter at the given +key+, otherwise raises an
-
# <tt>ActionController::ParameterMissing</tt> error.
-
#
-
# ActionController::Parameters.new(person: { name: 'Francesco' }).require(:person)
-
# # => {"name"=>"Francesco"}
-
#
-
# ActionController::Parameters.new(person: nil).require(:person)
-
# # => ActionController::ParameterMissing: param not found: person
-
#
-
# ActionController::Parameters.new(person: {}).require(:person)
-
# # => ActionController::ParameterMissing: param not found: person
-
1
def require(key)
-
self[key].presence || raise(ParameterMissing.new(key))
-
end
-
-
# Alias of #require.
-
1
alias :required :require
-
-
# Returns a new <tt>ActionController::Parameters</tt> instance that
-
# includes only the given +filters+ and sets the +permitted+ attribute
-
# for the object to +true+. This is useful for limiting which attributes
-
# should be allowed for mass updating.
-
#
-
# params = ActionController::Parameters.new(user: { name: 'Francesco', age: 22, role: 'admin' })
-
# permitted = params.require(:user).permit(:name, :age)
-
# permitted.permitted? # => true
-
# permitted.has_key?(:name) # => true
-
# permitted.has_key?(:age) # => true
-
# permitted.has_key?(:role) # => false
-
#
-
# You can also use +permit+ on nested parameters, like:
-
#
-
# params = ActionController::Parameters.new({
-
# person: {
-
# name: 'Francesco',
-
# age: 22,
-
# pets: [{
-
# name: 'Purplish',
-
# category: 'dogs'
-
# }]
-
# }
-
# })
-
#
-
# permitted = params.permit(person: [ :name, { pets: :name } ])
-
# permitted.permitted? # => true
-
# permitted[:person][:name] # => "Francesco"
-
# permitted[:person][:age] # => nil
-
# permitted[:person][:pets][0][:name] # => "Purplish"
-
# permitted[:person][:pets][0][:category] # => nil
-
#
-
# Note that if you use +permit+ in a key that points to a hash,
-
# it won't allow all the hash. You also need to specify which
-
# attributes inside the hash should be whitelisted.
-
#
-
# params = ActionController::Parameters.new({
-
# person: {
-
# contact: {
-
# email: 'none@test.com'
-
# phone: '555-1234'
-
# }
-
# }
-
# })
-
#
-
# params.require(:person).permit(:contact)
-
# # => {}
-
#
-
# params.require(:person).permit(contact: :phone)
-
# # => {"contact"=>{"phone"=>"555-1234"}}
-
#
-
# params.require(:person).permit(contact: [ :email, :phone ])
-
# # => {"contact"=>{"email"=>"none@test.com", "phone"=>"555-1234"}}
-
1
def permit(*filters)
-
params = self.class.new
-
-
filters.flatten.each do |filter|
-
case filter
-
when Symbol, String then
-
if has_key?(filter)
-
_value = self[filter]
-
params[filter] = _value unless Hash === _value
-
end
-
keys.grep(/\A#{Regexp.escape(filter)}\(\d+[if]?\)\z/) { |key| params[key] = self[key] }
-
when Hash then
-
self.slice(*filter.keys).each do |key, values|
-
return unless values
-
-
key = key.to_sym
-
-
params[key] = each_element(values) do |value|
-
# filters are a Hash, so we expect value to be a Hash too
-
next if filter.is_a?(Hash) && !value.is_a?(Hash)
-
-
value = self.class.new(value) if !value.respond_to?(:permit)
-
-
value.permit(*Array.wrap(filter[key]))
-
end
-
end
-
end
-
end
-
-
params.permit!
-
end
-
-
# Returns a parameter for the given +key+. If not found,
-
# returns +nil+.
-
#
-
# params = ActionController::Parameters.new(person: { name: 'Francesco' })
-
# params[:person] # => {"name"=>"Francesco"}
-
# params[:none] # => nil
-
1
def [](key)
-
convert_hashes_to_parameters(key, super)
-
end
-
-
# Returns a parameter for the given +key+. If the +key+
-
# can't be found, there are several options: With no other arguments,
-
# it will raise an <tt>ActionController::ParameterMissing</tt> error;
-
# if more arguments are given, then that will be returned; if a block
-
# is given, then that will be run and its result returned.
-
#
-
# params = ActionController::Parameters.new(person: { name: 'Francesco' })
-
# params.fetch(:person) # => {"name"=>"Francesco"}
-
# params.fetch(:none) # => ActionController::ParameterMissing: param not found: none
-
# params.fetch(:none, 'Francesco') # => "Francesco"
-
# params.fetch(:none) { 'Francesco' } # => "Francesco"
-
1
def fetch(key, *args)
-
convert_hashes_to_parameters(key, super)
-
rescue KeyError
-
raise ActionController::ParameterMissing.new(key)
-
end
-
-
# Returns a new <tt>ActionController::Parameters</tt> instance that
-
# includes only the given +keys+. If the given +keys+
-
# don't exist, returns an empty hash.
-
#
-
# params = ActionController::Parameters.new(a: 1, b: 2, c: 3)
-
# params.slice(:a, :b) # => {"a"=>1, "b"=>2}
-
# params.slice(:d) # => {}
-
1
def slice(*keys)
-
self.class.new(super).tap do |new_instance|
-
new_instance.instance_variable_set :@permitted, @permitted
-
end
-
end
-
-
# Returns an exact copy of the <tt>ActionController::Parameters</tt>
-
# instance. +permitted+ state is kept on the duped object.
-
#
-
# params = ActionController::Parameters.new(a: 1)
-
# params.permit!
-
# params.permitted? # => true
-
# copy_params = params.dup # => {"a"=>1}
-
# copy_params.permitted? # => true
-
1
def dup
-
super.tap do |duplicate|
-
duplicate.instance_variable_set :@permitted, @permitted
-
end
-
end
-
-
1
private
-
1
def convert_hashes_to_parameters(key, value)
-
if value.is_a?(Parameters) || !value.is_a?(Hash)
-
value
-
else
-
# Convert to Parameters on first access
-
self[key] = self.class.new(value)
-
end
-
end
-
-
1
def each_element(object)
-
if object.is_a?(Array)
-
object.map { |el| yield el }.compact
-
elsif object.is_a?(Hash) && object.keys.all? { |k| k =~ /\A-?\d+\z/ }
-
hash = object.class.new
-
object.each { |k,v| hash[k] = yield v }
-
hash
-
else
-
yield object
-
end
-
end
-
end
-
-
# == Strong \Parameters
-
#
-
# It provides an interface for protecting attributes from end-user
-
# assignment. This makes Action Controller parameters forbidden
-
# to be used in Active Model mass assignment until they have been
-
# whitelisted.
-
#
-
# In addition, parameters can be marked as required and flow through a
-
# predefined raise/rescue flow to end up as a 400 Bad Request with no
-
# effort.
-
#
-
# class PeopleController < ActionController::Base
-
# # Using "Person.create(params[:person])" would raise an
-
# # ActiveModel::ForbiddenAttributes exception because it'd
-
# # be using mass assignment without an explicit permit step.
-
# # This is the recommended form:
-
# def create
-
# Person.create(person_params)
-
# end
-
#
-
# # This will pass with flying colors as long as there's a person key in the
-
# # parameters, otherwise it'll raise an ActionController::MissingParameter
-
# # exception, which will get caught by ActionController::Base and turned
-
# # into a 400 Bad Request reply.
-
# def update
-
# redirect_to current_account.people.find(params[:id]).tap { |person|
-
# person.update_attributes!(person_params)
-
# }
-
# end
-
#
-
# private
-
# # Using a private method to encapsulate the permissible parameters is
-
# # just a good pattern since you'll be able to reuse the same permit
-
# # list between create and update. Also, you can specialize this method
-
# # with per-user checking of permissible attributes.
-
# def person_params
-
# params.require(:person).permit(:name, :age)
-
# end
-
# end
-
#
-
# In order to use <tt>accepts_nested_attribute_for</tt> with Strong \Parameters, you
-
# will need to specify which nested attributes should be whitelisted.
-
#
-
# class Person
-
# has_many :pets
-
# accepts_nested_attributes_for :pets
-
# end
-
#
-
# class PeopleController < ActionController::Base
-
# def create
-
# Person.create(person_params)
-
# end
-
#
-
# ...
-
#
-
# private
-
#
-
# def person_params
-
# # It's mandatory to specify the nested attributes that should be whitelisted.
-
# # If you use `permit` with just the key that points to the nested attributes hash,
-
# # it will return an empty hash.
-
# params.require(:person).permit(:name, :age, pets_attributes: [ :name, :category ])
-
# end
-
# end
-
#
-
# See ActionController::Parameters.require and ActionController::Parameters.permit
-
# for more information.
-
1
module StrongParameters
-
1
extend ActiveSupport::Concern
-
1
include ActiveSupport::Rescuable
-
-
1
included do
-
1
rescue_from(ActionController::ParameterMissing) do |parameter_missing_exception|
-
render text: "Required parameter missing: #{parameter_missing_exception.param}", status: :bad_request
-
end
-
end
-
-
# Returns a new ActionController::Parameters object that
-
# has been instantiated with the <tt>request.parameters</tt>.
-
1
def params
-
@_params ||= Parameters.new(request.parameters)
-
end
-
-
# Assigns the given +value+ to the +params+ hash. If +value+
-
# is a Hash, this will create an ActionController::Parameters
-
# object that has been instantiated with the given +value+ hash.
-
1
def params=(value)
-
@_params = value.is_a?(Hash) ? Parameters.new(value) : value
-
end
-
end
-
end
-
1
module ActionController
-
1
module Testing
-
1
extend ActiveSupport::Concern
-
-
1
include RackDelegation
-
-
# TODO : Rewrite tests using controller.headers= to use Rack env
-
1
def headers=(new_headers)
-
@_response ||= ActionDispatch::Response.new
-
@_response.headers.replace(new_headers)
-
end
-
-
# Behavior specific to functional tests
-
1
module Functional # :nodoc:
-
1
def set_response!(request)
-
end
-
-
1
def recycle!
-
@_url_options = nil
-
self.response_body = nil
-
self.formats = nil
-
self.params = nil
-
end
-
end
-
-
1
module ClassMethods
-
1
def before_filters
-
_process_action_callbacks.find_all{|x| x.kind == :before}.map{|x| x.name}
-
end
-
end
-
end
-
end
-
1
module ActionController
-
# Includes +url_for+ into the host class. The class has to provide a +RouteSet+ by implementing
-
# the <tt>_routes</tt> method. Otherwise, an exception will be raised.
-
#
-
# In addition to <tt>AbstractController::UrlFor</tt>, this module accesses the HTTP layer to define
-
# url options like the +host+. In order to do so, this module requires the host class
-
# to implement +env+ and +request+, which need to be a Rack-compatible.
-
#
-
# class RootUrl
-
# include ActionController::UrlFor
-
# include Rails.application.routes.url_helpers
-
#
-
# delegate :env, :request, to: :controller
-
#
-
# def initialize(controller)
-
# @controller = controller
-
# @url = root_path # named route from the application.
-
# end
-
# end
-
1
module UrlFor
-
1
extend ActiveSupport::Concern
-
-
1
include AbstractController::UrlFor
-
-
1
def url_options
-
@_url_options ||= super.reverse_merge(
-
:host => request.host,
-
:port => request.optional_port,
-
:protocol => request.protocol,
-
:_recall => request.symbolized_path_parameters
-
).freeze
-
-
if (same_origin = _routes.equal?(env["action_dispatch.routes"])) ||
-
(script_name = env["ROUTES_#{_routes.object_id}_SCRIPT_NAME"]) ||
-
(original_script_name = env['SCRIPT_NAME'])
-
@_url_options.dup.tap do |options|
-
if original_script_name
-
options[:original_script_name] = original_script_name
-
else
-
options[:script_name] = same_origin ? request.script_name.dup : script_name
-
end
-
options.freeze
-
end
-
else
-
@_url_options
-
end
-
end
-
end
-
end
-
1
module ActionController
-
1
module ModelNaming
-
# Converts the given object to an ActiveModel compliant one.
-
1
def convert_to_model(object)
-
object.respond_to?(:to_model) ? object.to_model : object
-
end
-
-
1
def model_name_from_record_or_class(record_or_class)
-
(record_or_class.is_a?(Class) ? record_or_class : convert_to_model(record_or_class).class).model_name
-
end
-
end
-
end
-
1
require 'active_support/deprecation'
-
1
require 'action_view/record_identifier'
-
-
1
module ActionController
-
1
module RecordIdentifier
-
1
MESSAGE = 'method will no longer be included by default in controllers since Rails 4.1. ' +
-
'If you would like to use it in controllers, please include ' +
-
'ActionView::RecodIdentifier module.'
-
-
1
def dom_id(record, prefix = nil)
-
ActiveSupport::Deprecation.warn('dom_id ' + MESSAGE)
-
ActionView::RecordIdentifier.dom_id(record, prefix)
-
end
-
-
1
def dom_class(record, prefix = nil)
-
ActiveSupport::Deprecation.warn('dom_class ' + MESSAGE)
-
ActionView::RecordIdentifier.dom_class(record, prefix)
-
end
-
end
-
end
-
1
require 'rack/session/abstract/id'
-
1
require 'active_support/core_ext/object/to_query'
-
1
require 'active_support/core_ext/module/anonymous'
-
-
1
module ActionController
-
1
module TemplateAssertions
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
2
setup :setup_subscriptions
-
2
teardown :teardown_subscriptions
-
end
-
-
1
def setup_subscriptions
-
1
@_partials = Hash.new(0)
-
1
@_templates = Hash.new(0)
-
1
@_layouts = Hash.new(0)
-
-
1
ActiveSupport::Notifications.subscribe("render_template.action_view") do |name, start, finish, id, payload|
-
2
path = payload[:layout]
-
2
if path
-
@_layouts[path] += 1
-
if path =~ /^layouts\/(.*)/
-
@_layouts[$1] += 1
-
end
-
end
-
end
-
-
1
ActiveSupport::Notifications.subscribe("!render_template.action_view") do |name, start, finish, id, payload|
-
1
path = payload[:virtual_path]
-
1
next unless path
-
1
partial = path =~ /^.*\/_[^\/]*$/
-
-
1
if partial
-
@_partials[path] += 1
-
@_partials[path.split("/").last] += 1
-
end
-
-
1
@_templates[path] += 1
-
end
-
end
-
-
1
def teardown_subscriptions
-
1
ActiveSupport::Notifications.unsubscribe("render_template.action_view")
-
1
ActiveSupport::Notifications.unsubscribe("!render_template.action_view")
-
end
-
-
1
def process(*args)
-
@_partials = Hash.new(0)
-
@_templates = Hash.new(0)
-
@_layouts = Hash.new(0)
-
super
-
end
-
-
# Asserts that the request was rendered with the appropriate template file or partials.
-
#
-
# # assert that the "new" view template was rendered
-
# assert_template "new"
-
#
-
# # assert that the exact template "admin/posts/new" was rendered
-
# assert_template %r{\Aadmin/posts/new\Z}
-
#
-
# # assert that the layout 'admin' was rendered
-
# assert_template layout: 'admin'
-
# assert_template layout: 'layouts/admin'
-
# assert_template layout: :admin
-
#
-
# # assert that no layout was rendered
-
# assert_template layout: nil
-
# assert_template layout: false
-
#
-
# # assert that the "_customer" partial was rendered twice
-
# assert_template partial: '_customer', count: 2
-
#
-
# # assert that no partials were rendered
-
# assert_template partial: false
-
#
-
# In a view test case, you can also assert that specific locals are passed
-
# to partials:
-
#
-
# # assert that the "_customer" partial was rendered with a specific object
-
# assert_template partial: '_customer', locals: { customer: @customer }
-
1
def assert_template(options = {}, message = nil)
-
# Force body to be read in case the
-
# template is being streamed
-
response.body
-
-
case options
-
when NilClass, Regexp, String, Symbol
-
options = options.to_s if Symbol === options
-
rendered = @_templates
-
msg = message || sprintf("expecting <%s> but rendering with <%s>",
-
options.inspect, rendered.keys)
-
matches_template =
-
case options
-
when String
-
rendered.any? do |t, num|
-
options_splited = options.split(File::SEPARATOR)
-
t_splited = t.split(File::SEPARATOR)
-
t_splited.last(options_splited.size) == options_splited
-
end
-
when Regexp
-
rendered.any? { |t,num| t.match(options) }
-
when NilClass
-
rendered.blank?
-
end
-
assert matches_template, msg
-
when Hash
-
if options.key?(:layout)
-
expected_layout = options[:layout]
-
msg = message || sprintf("expecting layout <%s> but action rendered <%s>",
-
expected_layout, @_layouts.keys)
-
-
case expected_layout
-
when String, Symbol
-
assert_includes @_layouts.keys, expected_layout.to_s, msg
-
when Regexp
-
assert(@_layouts.keys.any? {|l| l =~ expected_layout }, msg)
-
when nil, false
-
assert(@_layouts.empty?, msg)
-
end
-
end
-
-
if expected_partial = options[:partial]
-
if expected_locals = options[:locals]
-
if defined?(@_rendered_views)
-
view = expected_partial.to_s.sub(/^_/,'')
-
msg = 'expecting %s to be rendered with %s but was with %s' % [expected_partial,
-
expected_locals,
-
@_rendered_views.locals_for(view)]
-
assert(@_rendered_views.view_rendered?(view, options[:locals]), msg)
-
else
-
warn "the :locals option to #assert_template is only supported in a ActionView::TestCase"
-
end
-
elsif expected_count = options[:count]
-
actual_count = @_partials[expected_partial]
-
msg = message || sprintf("expecting %s to be rendered %s time(s) but rendered %s time(s)",
-
expected_partial, expected_count, actual_count)
-
assert(actual_count == expected_count.to_i, msg)
-
else
-
msg = message || sprintf("expecting partial <%s> but action rendered <%s>",
-
options[:partial], @_partials.keys)
-
assert_includes @_partials, expected_partial, msg
-
end
-
elsif options.key?(:partial)
-
assert @_partials.empty?,
-
"Expected no partials to be rendered"
-
end
-
else
-
raise ArgumentError, "assert_template only accepts a String, Symbol, Hash, Regexp, or nil"
-
end
-
end
-
end
-
-
1
class TestRequest < ActionDispatch::TestRequest #:nodoc:
-
1
DEFAULT_ENV = ActionDispatch::TestRequest::DEFAULT_ENV.dup
-
1
DEFAULT_ENV.delete 'PATH_INFO'
-
-
1
def initialize(env = {})
-
super
-
-
self.session = TestSession.new
-
self.session_options = TestSession::DEFAULT_OPTIONS.merge(:id => SecureRandom.hex(16))
-
end
-
-
1
def assign_parameters(routes, controller_path, action, parameters = {})
-
parameters = parameters.symbolize_keys.merge(:controller => controller_path, :action => action)
-
extra_keys = routes.extra_keys(parameters)
-
non_path_parameters = get? ? query_parameters : request_parameters
-
parameters.each do |key, value|
-
if value.is_a?(Array) && (value.frozen? || value.any?(&:frozen?))
-
value = value.map{ |v| v.duplicable? ? v.dup : v }
-
elsif value.is_a?(Hash) && (value.frozen? || value.any?{ |k,v| v.frozen? })
-
value = Hash[value.map{ |k,v| [k, v.duplicable? ? v.dup : v] }]
-
elsif value.frozen? && value.duplicable?
-
value = value.dup
-
end
-
-
if extra_keys.include?(key.to_sym)
-
non_path_parameters[key] = value
-
else
-
if value.is_a?(Array)
-
value = value.map(&:to_param)
-
else
-
value = value.to_param
-
end
-
-
path_parameters[key.to_s] = value
-
end
-
end
-
-
# Clear the combined params hash in case it was already referenced.
-
@env.delete("action_dispatch.request.parameters")
-
-
params = self.request_parameters.dup
-
%w(controller action only_path).each do |k|
-
params.delete(k)
-
params.delete(k.to_sym)
-
end
-
data = params.to_query
-
-
@env['CONTENT_LENGTH'] = data.length.to_s
-
@env['rack.input'] = StringIO.new(data)
-
end
-
-
1
def recycle!
-
@formats = nil
-
@env.delete_if { |k, v| k =~ /^(action_dispatch|rack)\.request/ }
-
@env.delete_if { |k, v| k =~ /^action_dispatch\.rescue/ }
-
@symbolized_path_params = nil
-
@method = @request_method = nil
-
@fullpath = @ip = @remote_ip = @protocol = nil
-
@env['action_dispatch.request.query_parameters'] = {}
-
@set_cookies ||= {}
-
@set_cookies.update(Hash[cookie_jar.instance_variable_get("@set_cookies").map{ |k,o| [k,o[:value]] }])
-
deleted_cookies = cookie_jar.instance_variable_get("@delete_cookies")
-
@set_cookies.reject!{ |k,v| deleted_cookies.include?(k) }
-
cookie_jar.update(rack_cookies)
-
cookie_jar.update(cookies)
-
cookie_jar.update(@set_cookies)
-
cookie_jar.recycle!
-
end
-
-
1
private
-
-
1
def default_env
-
DEFAULT_ENV
-
end
-
end
-
-
1
class TestResponse < ActionDispatch::TestResponse
-
1
def recycle!
-
initialize
-
end
-
end
-
-
1
class TestSession < Rack::Session::Abstract::SessionHash #:nodoc:
-
1
DEFAULT_OPTIONS = Rack::Session::Abstract::ID::DEFAULT_OPTIONS
-
-
1
def initialize(session = {})
-
super(nil, nil)
-
replace(session.stringify_keys)
-
@loaded = true
-
end
-
-
1
def exists?
-
true
-
end
-
end
-
-
# Superclass for ActionController functional tests. Functional tests allow you to
-
# test a single controller action per test method. This should not be confused with
-
# integration tests (see ActionDispatch::IntegrationTest), which are more like
-
# "stories" that can involve multiple controllers and multiple actions (i.e. multiple
-
# different HTTP requests).
-
#
-
# == Basic example
-
#
-
# Functional tests are written as follows:
-
# 1. First, one uses the +get+, +post+, +patch+, +put+, +delete+ or +head+ method to simulate
-
# an HTTP request.
-
# 2. Then, one asserts whether the current state is as expected. "State" can be anything:
-
# the controller's HTTP response, the database contents, etc.
-
#
-
# For example:
-
#
-
# class BooksControllerTest < ActionController::TestCase
-
# def test_create
-
# # Simulate a POST response with the given HTTP parameters.
-
# post(:create, book: { title: "Love Hina" })
-
#
-
# # Assert that the controller tried to redirect us to
-
# # the created book's URI.
-
# assert_response :found
-
#
-
# # Assert that the controller really put the book in the database.
-
# assert_not_nil Book.find_by_title("Love Hina")
-
# end
-
# end
-
#
-
# You can also send a real document in the simulated HTTP request.
-
#
-
# def test_create
-
# json = {book: { title: "Love Hina" }}.to_json
-
# post :create, json
-
# end
-
#
-
# == Special instance variables
-
#
-
# ActionController::TestCase will also automatically provide the following instance
-
# variables for use in the tests:
-
#
-
# <b>@controller</b>::
-
# The controller instance that will be tested.
-
# <b>@request</b>::
-
# An ActionController::TestRequest, representing the current HTTP
-
# request. You can modify this object before sending the HTTP request. For example,
-
# you might want to set some session properties before sending a GET request.
-
# <b>@response</b>::
-
# An ActionController::TestResponse object, representing the response
-
# of the last HTTP response. In the above example, <tt>@response</tt> becomes valid
-
# after calling +post+. If the various assert methods are not sufficient, then you
-
# may use this object to inspect the HTTP response in detail.
-
#
-
# (Earlier versions of \Rails required each functional test to subclass
-
# Test::Unit::TestCase and define @controller, @request, @response in +setup+.)
-
#
-
# == Controller is automatically inferred
-
#
-
# ActionController::TestCase will automatically infer the controller under test
-
# from the test class name. If the controller cannot be inferred from the test
-
# class name, you can explicitly set it with +tests+.
-
#
-
# class SpecialEdgeCaseWidgetsControllerTest < ActionController::TestCase
-
# tests WidgetController
-
# end
-
#
-
# == \Testing controller internals
-
#
-
# In addition to these specific assertions, you also have easy access to various collections that the regular test/unit assertions
-
# can be used against. These collections are:
-
#
-
# * assigns: Instance variables assigned in the action that are available for the view.
-
# * session: Objects being saved in the session.
-
# * flash: The flash objects currently in the session.
-
# * cookies: \Cookies being sent to the user on this request.
-
#
-
# These collections can be used just like any other hash:
-
#
-
# assert_not_nil assigns(:person) # makes sure that a @person instance variable was set
-
# assert_equal "Dave", cookies[:name] # makes sure that a cookie called :name was set as "Dave"
-
# assert flash.empty? # makes sure that there's nothing in the flash
-
#
-
# For historic reasons, the assigns hash uses string-based keys. So <tt>assigns[:person]</tt> won't work, but <tt>assigns["person"]</tt> will. To
-
# appease our yearning for symbols, though, an alternative accessor has been devised using a method call instead of index referencing.
-
# So <tt>assigns(:person)</tt> will work just like <tt>assigns["person"]</tt>, but again, <tt>assigns[:person]</tt> will not work.
-
#
-
# On top of the collections, you have the complete url that a given action redirected to available in <tt>redirect_to_url</tt>.
-
#
-
# For redirects within the same controller, you can even call follow_redirect and the redirect will be followed, triggering another
-
# action call which can then be asserted against.
-
#
-
# == Manipulating session and cookie variables
-
#
-
# Sometimes you need to set up the session and cookie variables for a test.
-
# To do this just assign a value to the session or cookie collection:
-
#
-
# session[:key] = "value"
-
# cookies[:key] = "value"
-
#
-
# To clear the cookies for a test just clear the cookie collection:
-
#
-
# cookies.clear
-
#
-
# == \Testing named routes
-
#
-
# If you're using named routes, they can be easily tested using the original named routes' methods straight in the test case.
-
#
-
# assert_redirected_to page_url(title: 'foo')
-
1
class TestCase < ActiveSupport::TestCase
-
-
# Use AC::TestCase for the base class when describing a controller
-
1
register_spec_type(self) do |desc|
-
31
Class === desc && desc < ActionController::Metal
-
end
-
1
register_spec_type(/Controller( ?Test)?\z/i, self)
-
-
1
module Behavior
-
1
extend ActiveSupport::Concern
-
1
include ActionDispatch::TestProcess
-
1
include ActiveSupport::Testing::ConstantLookup
-
-
1
attr_reader :response, :request
-
-
1
module ClassMethods
-
-
# Sets the controller class name. Useful if the name can't be inferred from test class.
-
# Normalizes +controller_class+ before using.
-
#
-
# tests WidgetController
-
# tests :widget
-
# tests 'widget'
-
1
def tests(controller_class)
-
case controller_class
-
when String, Symbol
-
self.controller_class = "#{controller_class.to_s.camelize}Controller".constantize
-
when Class
-
self.controller_class = controller_class
-
else
-
raise ArgumentError, "controller class must be a String, Symbol, or Class"
-
end
-
end
-
-
1
def controller_class=(new_class)
-
prepare_controller_class(new_class) if new_class
-
self._controller_class = new_class
-
end
-
-
1
def controller_class
-
if current_controller_class = self._controller_class
-
current_controller_class
-
else
-
self.controller_class = determine_default_controller_class(name)
-
end
-
end
-
-
1
def determine_default_controller_class(name)
-
determine_constant_from_test_name(name) do |constant|
-
Class === constant && constant < ActionController::Metal
-
end
-
end
-
-
1
def prepare_controller_class(new_class)
-
new_class.send :include, ActionController::TestCase::RaiseActionExceptions
-
end
-
-
end
-
-
# Executes a request simulating GET HTTP method and set/volley the response
-
1
def get(action, *args)
-
process(action, "GET", *args)
-
end
-
-
# Executes a request simulating POST HTTP method and set/volley the response
-
1
def post(action, *args)
-
process(action, "POST", *args)
-
end
-
-
# Executes a request simulating PATCH HTTP method and set/volley the response
-
1
def patch(action, *args)
-
process(action, "PATCH", *args)
-
end
-
-
# Executes a request simulating PUT HTTP method and set/volley the response
-
1
def put(action, *args)
-
process(action, "PUT", *args)
-
end
-
-
# Executes a request simulating DELETE HTTP method and set/volley the response
-
1
def delete(action, *args)
-
process(action, "DELETE", *args)
-
end
-
-
# Executes a request simulating HEAD HTTP method and set/volley the response
-
1
def head(action, *args)
-
process(action, "HEAD", *args)
-
end
-
-
# Executes a request simulating OPTIONS HTTP method and set/volley the response
-
1
def options(action, *args)
-
process(action, "OPTIONS", *args)
-
end
-
-
1
def xml_http_request(request_method, action, parameters = nil, session = nil, flash = nil)
-
@request.env['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest'
-
@request.env['HTTP_ACCEPT'] ||= [Mime::JS, Mime::HTML, Mime::XML, 'text/xml', Mime::ALL].join(', ')
-
__send__(request_method, action, parameters, session, flash).tap do
-
@request.env.delete 'HTTP_X_REQUESTED_WITH'
-
@request.env.delete 'HTTP_ACCEPT'
-
end
-
end
-
1
alias xhr :xml_http_request
-
-
1
def paramify_values(hash_or_array_or_value)
-
case hash_or_array_or_value
-
when Hash
-
Hash[hash_or_array_or_value.map{|key, value| [key, paramify_values(value)] }]
-
when Array
-
hash_or_array_or_value.map {|i| paramify_values(i)}
-
when Rack::Test::UploadedFile, ActionDispatch::Http::UploadedFile
-
hash_or_array_or_value
-
else
-
hash_or_array_or_value.to_param
-
end
-
end
-
-
1
def process(action, http_method = 'GET', *args)
-
check_required_ivars
-
http_method, args = handle_old_process_api(http_method, args, caller)
-
-
if args.first.is_a?(String) && http_method != 'HEAD'
-
@request.env['RAW_POST_DATA'] = args.shift
-
end
-
-
parameters, session, flash = args
-
-
# Ensure that numbers and symbols passed as params are converted to
-
# proper params, as is the case when engaging rack.
-
parameters = paramify_values(parameters) if html_format?(parameters)
-
-
@html_document = nil
-
-
unless @controller.respond_to?(:recycle!)
-
@controller.extend(Testing::Functional)
-
@controller.class.class_eval { include Testing }
-
end
-
-
@request.recycle!
-
@response.recycle!
-
@controller.recycle!
-
-
@request.env['REQUEST_METHOD'] = http_method
-
-
parameters ||= {}
-
controller_class_name = @controller.class.anonymous? ?
-
"anonymous" :
-
@controller.class.name.underscore.sub(/_controller$/, '')
-
-
@request.assign_parameters(@routes, controller_class_name, action.to_s, parameters)
-
-
@request.session.update(session) if session
-
@request.session["flash"] = @request.flash.update(flash || {})
-
-
@controller.request = @request
-
@controller.response = @response
-
-
build_request_uri(action, parameters)
-
-
name = @request.parameters[:action]
-
-
@controller.process(name)
-
-
if cookies = @request.env['action_dispatch.cookies']
-
cookies.write(@response)
-
end
-
@response.prepare!
-
-
@assigns = @controller.respond_to?(:view_assigns) ? @controller.view_assigns : {}
-
@request.session.delete('flash') if @request.session['flash'].blank?
-
@response
-
end
-
-
1
def setup_controller_request_and_response
-
@request = build_request
-
@response = build_response
-
@response.request = @request
-
-
@controller = nil unless defined? @controller
-
-
if klass = self.class.controller_class
-
unless @controller
-
begin
-
@controller = klass.new
-
rescue
-
warn "could not construct controller #{klass}" if $VERBOSE
-
end
-
end
-
end
-
-
if @controller
-
@controller.request = @request
-
@controller.params = {}
-
end
-
end
-
-
1
def build_request
-
TestRequest.new
-
end
-
-
1
def build_response
-
TestResponse.new
-
end
-
-
1
included do
-
1
include ActionController::TemplateAssertions
-
1
include ActionDispatch::Assertions
-
1
class_attribute :_controller_class
-
1
setup :setup_controller_request_and_response
-
end
-
-
1
private
-
1
def check_required_ivars
-
# Sanity check for required instance variables so we can give an
-
# understandable error message.
-
[:@routes, :@controller, :@request, :@response].each do |iv_name|
-
if !instance_variable_defined?(iv_name) || instance_variable_get(iv_name).nil?
-
raise "#{iv_name} is nil: make sure you set it in your test's setup method."
-
end
-
end
-
end
-
-
1
def handle_old_process_api(http_method, args, callstack)
-
# 4.0: Remove this method.
-
if http_method.is_a?(Hash)
-
ActiveSupport::Deprecation.warn("TestCase#process now expects the HTTP method as second argument: process(action, http_method, params, session, flash)", callstack)
-
args.unshift(http_method)
-
http_method = args.last.is_a?(String) ? args.last : "GET"
-
end
-
-
[http_method, args]
-
end
-
-
1
def build_request_uri(action, parameters)
-
unless @request.env["PATH_INFO"]
-
options = @controller.respond_to?(:url_options) ? @controller.__send__(:url_options).merge(parameters) : parameters
-
options.update(
-
:only_path => true,
-
:action => action,
-
:relative_url_root => nil,
-
:_recall => @request.symbolized_path_parameters)
-
-
url, query_string = @routes.url_for(options).split("?", 2)
-
-
@request.env["SCRIPT_NAME"] = @controller.config.relative_url_root
-
@request.env["PATH_INFO"] = url
-
@request.env["QUERY_STRING"] = query_string || ""
-
end
-
end
-
-
1
def html_format?(parameters)
-
return true unless parameters.is_a?(Hash)
-
Mime.fetch(parameters[:format]) { Mime['html'] }.html?
-
end
-
end
-
-
# When the request.remote_addr remains the default for testing, which is 0.0.0.0, the exception is simply raised inline
-
# (skipping the regular exception handling from rescue_action). If the request.remote_addr is anything else, the regular
-
# rescue_action process takes place. This means you can test your rescue_action code by setting remote_addr to something else
-
# than 0.0.0.0.
-
#
-
# The exception is stored in the exception accessor for further inspection.
-
1
module RaiseActionExceptions
-
1
def self.included(base) #:nodoc:
-
unless base.method_defined?(:exception) && base.method_defined?(:exception=)
-
base.class_eval do
-
attr_accessor :exception
-
protected :exception, :exception=
-
end
-
end
-
end
-
-
1
protected
-
1
def rescue_action_without_handler(e)
-
self.exception = e
-
-
if request.remote_addr == "0.0.0.0"
-
raise(e)
-
else
-
super(e)
-
end
-
end
-
end
-
-
1
include Behavior
-
end
-
end
-
#--
-
# Copyright (c) 2004-2012 David Heinemeier Hansson
-
#
-
# Permission is hereby granted, free of charge, to any person obtaining
-
# a copy of this software and associated documentation files (the
-
# "Software"), to deal in the Software without restriction, including
-
# without limitation the rights to use, copy, modify, merge, publish,
-
# distribute, sublicense, and/or sell copies of the Software, and to
-
# permit persons to whom the Software is furnished to do so, subject to
-
# the following conditions:
-
#
-
# The above copyright notice and this permission notice shall be
-
# included in all copies or substantial portions of the Software.
-
#
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
#++
-
-
1
require 'active_support'
-
1
require 'active_support/rails'
-
1
require 'active_support/core_ext/module/attribute_accessors'
-
-
1
require 'action_pack'
-
1
require 'rack'
-
-
1
module Rack
-
1
autoload :Test, 'rack/test'
-
end
-
-
1
module ActionDispatch
-
1
extend ActiveSupport::Autoload
-
-
1
class IllegalStateError < StandardError
-
end
-
-
1
eager_autoload do
-
1
autoload_under 'http' do
-
1
autoload :Request
-
1
autoload :Response
-
end
-
end
-
-
1
autoload_under 'middleware' do
-
1
autoload :RequestId
-
1
autoload :BestStandardsSupport
-
1
autoload :Callbacks
-
1
autoload :Cookies
-
1
autoload :DebugExceptions
-
1
autoload :ExceptionWrapper
-
1
autoload :Flash
-
1
autoload :Head
-
1
autoload :ParamsParser
-
1
autoload :PublicExceptions
-
1
autoload :Reloader
-
1
autoload :RemoteIp
-
1
autoload :ShowExceptions
-
1
autoload :SSL
-
1
autoload :Static
-
end
-
-
1
autoload :MiddlewareStack, 'action_dispatch/middleware/stack'
-
1
autoload :Routing
-
-
1
module Http
-
1
extend ActiveSupport::Autoload
-
-
1
autoload :Cache
-
1
autoload :Headers
-
1
autoload :MimeNegotiation
-
1
autoload :Parameters
-
1
autoload :ParameterFilter
-
1
autoload :FilterParameters
-
1
autoload :Upload
-
1
autoload :UploadedFile, 'action_dispatch/http/upload'
-
1
autoload :URL
-
end
-
-
1
module Session
-
1
autoload :AbstractStore, 'action_dispatch/middleware/session/abstract_store'
-
1
autoload :CookieStore, 'action_dispatch/middleware/session/cookie_store'
-
1
autoload :MemCacheStore, 'action_dispatch/middleware/session/mem_cache_store'
-
1
autoload :CacheStore, 'action_dispatch/middleware/session/cache_store'
-
end
-
-
1
mattr_accessor :test_app
-
-
1
autoload_under 'testing' do
-
1
autoload :Assertions
-
1
autoload :Integration
-
1
autoload :IntegrationTest, 'action_dispatch/testing/integration'
-
1
autoload :PerformanceTest
-
1
autoload :TestProcess
-
1
autoload :TestRequest
-
1
autoload :TestResponse
-
end
-
end
-
-
1
autoload :Mime, 'action_dispatch/http/mime_type'
-
-
1
ActiveSupport.on_load(:action_view) do
-
1
ActionView::Base.default_formats ||= Mime::SET.symbols
-
1
ActionView::Template::Types.delegate_to Mime
-
end
-
-
1
module ActionDispatch
-
1
module Http
-
1
module Cache
-
1
module Request
-
-
1
HTTP_IF_MODIFIED_SINCE = 'HTTP_IF_MODIFIED_SINCE'.freeze
-
1
HTTP_IF_NONE_MATCH = 'HTTP_IF_NONE_MATCH'.freeze
-
-
1
def if_modified_since
-
if since = env[HTTP_IF_MODIFIED_SINCE]
-
Time.rfc2822(since) rescue nil
-
end
-
end
-
-
1
def if_none_match
-
env[HTTP_IF_NONE_MATCH]
-
end
-
-
1
def if_none_match_etags
-
(if_none_match ? if_none_match.split(/\s*,\s*/) : []).collect do |etag|
-
etag.gsub(/^\"|\"$/, "")
-
end
-
end
-
-
1
def not_modified?(modified_at)
-
if_modified_since && modified_at && if_modified_since >= modified_at
-
end
-
-
1
def etag_matches?(etag)
-
if etag
-
etag = etag.gsub(/^\"|\"$/, "")
-
if_none_match_etags.include?(etag)
-
end
-
end
-
-
# Check response freshness (Last-Modified and ETag) against request
-
# If-Modified-Since and If-None-Match conditions. If both headers are
-
# supplied, both must match, or the request is not considered fresh.
-
1
def fresh?(response)
-
last_modified = if_modified_since
-
etag = if_none_match
-
-
return false unless last_modified || etag
-
-
success = true
-
success &&= not_modified?(response.last_modified) if last_modified
-
success &&= etag_matches?(response.etag) if etag
-
success
-
end
-
end
-
-
1
module Response
-
1
attr_reader :cache_control, :etag
-
1
alias :etag? :etag
-
-
1
def last_modified
-
if last = headers[LAST_MODIFIED]
-
Time.httpdate(last)
-
end
-
end
-
-
1
def last_modified?
-
1
headers.include?(LAST_MODIFIED)
-
end
-
-
1
def last_modified=(utc_time)
-
headers[LAST_MODIFIED] = utc_time.httpdate
-
end
-
-
1
def date
-
if date_header = headers['Date']
-
Time.httpdate(date_header)
-
end
-
end
-
-
1
def date?
-
headers.include?('Date')
-
end
-
-
1
def date=(utc_time)
-
headers['Date'] = utc_time.httpdate
-
end
-
-
1
def etag=(etag)
-
key = ActiveSupport::Cache.expand_cache_key(etag)
-
@etag = self[ETAG] = %("#{Digest::MD5.hexdigest(key)}")
-
end
-
-
1
private
-
-
1
LAST_MODIFIED = "Last-Modified".freeze
-
1
ETAG = "ETag".freeze
-
1
CACHE_CONTROL = "Cache-Control".freeze
-
1
SPESHUL_KEYS = %w[extras no-cache max-age public must-revalidate]
-
-
1
def cache_control_segments
-
2
if cache_control = self[CACHE_CONTROL]
-
cache_control.delete(' ').split(',')
-
else
-
2
[]
-
end
-
end
-
-
1
def cache_control_headers
-
2
cache_control = {}
-
-
2
cache_control_segments.each do |segment|
-
directive, argument = segment.split('=', 2)
-
-
if SPESHUL_KEYS.include? directive
-
key = directive.tr('-', '_')
-
cache_control[key.to_sym] = argument || true
-
else
-
cache_control[:extras] ||= []
-
cache_control[:extras] << segment
-
end
-
end
-
-
2
cache_control
-
end
-
-
1
def prepare_cache_control!
-
2
@cache_control = cache_control_headers
-
2
@etag = self[ETAG]
-
end
-
-
1
def handle_conditional_get!
-
1
if etag? || last_modified? || !@cache_control.empty?
-
set_conditional_cache_control!
-
end
-
end
-
-
1
DEFAULT_CACHE_CONTROL = "max-age=0, private, must-revalidate".freeze
-
1
NO_CACHE = "no-cache".freeze
-
1
PUBLIC = "public".freeze
-
1
PRIVATE = "private".freeze
-
1
MUST_REVALIDATE = "must-revalidate".freeze
-
-
1
def set_conditional_cache_control!
-
control = {}
-
cc_headers = cache_control_headers
-
if extras = cc_headers.delete(:extras)
-
@cache_control[:extras] ||= []
-
@cache_control[:extras] += extras
-
@cache_control[:extras].uniq!
-
end
-
-
control.merge! cc_headers
-
control.merge! @cache_control
-
-
if control.empty?
-
headers[CACHE_CONTROL] = DEFAULT_CACHE_CONTROL
-
elsif control[:no_cache]
-
headers[CACHE_CONTROL] = NO_CACHE
-
if control[:extras]
-
headers[CACHE_CONTROL] += ", #{control[:extras].join(', ')}"
-
end
-
else
-
extras = control[:extras]
-
max_age = control[:max_age]
-
-
options = []
-
options << "max-age=#{max_age.to_i}" if max_age
-
options << (control[:public] ? PUBLIC : PRIVATE)
-
options << MUST_REVALIDATE if control[:must_revalidate]
-
options.concat(extras) if extras
-
-
headers[CACHE_CONTROL] = options.join(", ")
-
end
-
end
-
end
-
end
-
end
-
end
-
1
require 'mutex_m'
-
1
require 'active_support/core_ext/hash/keys'
-
1
require 'active_support/core_ext/object/duplicable'
-
-
1
module ActionDispatch
-
1
module Http
-
# Allows you to specify sensitive parameters which will be replaced from
-
# the request log by looking in the query string of the request and all
-
# subhashes of the params hash to filter. If a block is given, each key and
-
# value of the params hash and all subhashes is passed to it, the value
-
# or key can be replaced using String#replace or similar method.
-
#
-
# env["action_dispatch.parameter_filter"] = [:password]
-
# => replaces the value to all keys matching /password/i with "[FILTERED]"
-
#
-
# env["action_dispatch.parameter_filter"] = [:foo, "bar"]
-
# => replaces the value to all keys matching /foo|bar/i with "[FILTERED]"
-
#
-
# env["action_dispatch.parameter_filter"] = lambda do |k,v|
-
# v.reverse! if k =~ /secret/i
-
# end
-
# => reverses the value to all keys matching /secret/i
-
1
module FilterParameters
-
1
@@parameter_filter_for = {}.extend(Mutex_m)
-
-
1
ENV_MATCH = [/RAW_POST_DATA/, "rack.request.form_vars"] # :nodoc:
-
1
NULL_PARAM_FILTER = ParameterFilter.new # :nodoc:
-
1
NULL_ENV_FILTER = ParameterFilter.new ENV_MATCH # :nodoc:
-
-
1
def initialize(env)
-
3
super
-
3
@filtered_parameters = nil
-
3
@filtered_env = nil
-
3
@filtered_path = nil
-
end
-
-
# Return a hash of parameters with all sensitive data replaced.
-
1
def filtered_parameters
-
1
@filtered_parameters ||= parameter_filter.filter(parameters)
-
end
-
-
# Return a hash of request.env with all sensitive data replaced.
-
1
def filtered_env
-
@filtered_env ||= env_filter.filter(@env)
-
end
-
-
# Reconstructed a path with all sensitive GET parameters replaced.
-
1
def filtered_path
-
@filtered_path ||= query_string.empty? ? path : "#{path}?#{filtered_query_string}"
-
end
-
-
1
protected
-
-
1
def parameter_filter
-
parameter_filter_for @env.fetch("action_dispatch.parameter_filter") {
-
1
return NULL_PARAM_FILTER
-
1
}
-
end
-
-
1
def env_filter
-
user_key = @env.fetch("action_dispatch.parameter_filter") {
-
return NULL_ENV_FILTER
-
}
-
parameter_filter_for(Array(user_key) + ENV_MATCH)
-
end
-
-
1
def parameter_filter_for(filters)
-
@@parameter_filter_for.synchronize do
-
# Do we *actually* need this cache? Constructing ParameterFilters
-
# doesn't seem too expensive.
-
@@parameter_filter_for[filters] ||= ParameterFilter.new(filters)
-
end
-
end
-
-
1
KV_RE = '[^&;=]+'
-
1
PAIR_RE = %r{(#{KV_RE})=(#{KV_RE})}
-
1
def filtered_query_string
-
query_string.gsub(PAIR_RE) do |_|
-
parameter_filter.filter([[$1, $2]]).first.join("=")
-
end
-
end
-
-
end
-
end
-
end
-
1
module ActionDispatch
-
1
module Http
-
1
class Headers
-
1
include Enumerable
-
-
1
def initialize(env = {})
-
@headers = env
-
end
-
-
1
def [](header_name)
-
@headers[env_name(header_name)]
-
end
-
-
1
def []=(k,v); @headers[k] = v; end
-
1
def key?(k); @headers.key? k; end
-
1
alias :include? :key?
-
-
1
def fetch(header_name, *args, &block)
-
@headers.fetch env_name(header_name), *args, &block
-
end
-
-
1
def each(&block)
-
@headers.each(&block)
-
end
-
-
1
private
-
-
# Converts a HTTP header name to an environment variable name if it is
-
# not contained within the headers hash.
-
1
def env_name(header_name)
-
@headers.include?(header_name) ? header_name : cgi_name(header_name)
-
end
-
-
1
def cgi_name(k)
-
"HTTP_#{k.upcase.gsub(/-/, '_')}"
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/module/attribute_accessors'
-
-
1
module ActionDispatch
-
1
module Http
-
1
module MimeNegotiation
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
1
mattr_accessor :ignore_accept_header
-
1
self.ignore_accept_header = false
-
end
-
-
# The MIME type of the HTTP request, such as Mime::XML.
-
#
-
# For backward compatibility, the post \format is extracted from the
-
# X-Post-Data-Format HTTP header if present.
-
1
def content_mime_type
-
2
@env["action_dispatch.request.content_type"] ||= begin
-
1
if @env['CONTENT_TYPE'] =~ /^([^,\;]*)/
-
1
Mime::Type.lookup($1.strip.downcase)
-
else
-
nil
-
end
-
end
-
end
-
-
1
def content_type
-
content_mime_type && content_mime_type.to_s
-
end
-
-
# Returns the accepted MIME type for the request.
-
1
def accepts
-
@env["action_dispatch.request.accepts"] ||= begin
-
header = @env['HTTP_ACCEPT'].to_s.strip
-
-
if header.empty?
-
[content_mime_type]
-
else
-
Mime::Type.parse(header)
-
end
-
end
-
end
-
-
# Returns the MIME type for the \format used in the request.
-
#
-
# GET /posts/5.xml | request.format => Mime::XML
-
# GET /posts/5.xhtml | request.format => Mime::HTML
-
# GET /posts/5 | request.format => Mime::HTML or MIME::JS, or request.accepts.first
-
#
-
1
def format(view_path = [])
-
1
formats.first
-
end
-
-
1
def formats
-
2
@env["action_dispatch.request.formats"] ||=
-
if parameters[:format]
-
Array(Mime[parameters[:format]])
-
elsif use_accept_header && valid_accept_header
-
accepts
-
elsif xhr?
-
[Mime::JS]
-
else
-
1
[Mime::HTML]
-
end
-
end
-
-
# Sets the \format by string extension, which can be used to force custom formats
-
# that are not controlled by the extension.
-
#
-
# class ApplicationController < ActionController::Base
-
# before_filter :adjust_format_for_iphone
-
#
-
# private
-
# def adjust_format_for_iphone
-
# request.format = :iphone if request.env["HTTP_USER_AGENT"][/iPhone/]
-
# end
-
# end
-
1
def format=(extension)
-
parameters[:format] = extension.to_s
-
@env["action_dispatch.request.formats"] = [Mime::Type.lookup_by_extension(parameters[:format])]
-
end
-
-
# Sets the \formats by string extensions. This differs from #format= by allowing you
-
# to set multiple, ordered formats, which is useful when you want to have a fallback.
-
#
-
# In this example, the :iphone format will be used if it's available, otherwise it'll fallback
-
# to the :html format.
-
#
-
# class ApplicationController < ActionController::Base
-
# before_filter :adjust_format_for_iphone_with_html_fallback
-
#
-
# private
-
# def adjust_format_for_iphone_with_html_fallback
-
# request.formats = [ :iphone, :html ] if request.env["HTTP_USER_AGENT"][/iPhone/]
-
# end
-
# end
-
1
def formats=(extensions)
-
parameters[:format] = extensions.first.to_s
-
@env["action_dispatch.request.formats"] = extensions.collect do |extension|
-
Mime::Type.lookup_by_extension(extension)
-
end
-
end
-
-
# Receives an array of mimes and return the first user sent mime that
-
# matches the order array.
-
#
-
1
def negotiate_mime(order)
-
formats.each do |priority|
-
if priority == Mime::ALL
-
return order.first
-
elsif order.include?(priority)
-
return priority
-
end
-
end
-
-
order.include?(Mime::ALL) ? formats.first : nil
-
end
-
-
1
protected
-
-
1
BROWSER_LIKE_ACCEPTS = /,\s*\*\/\*|\*\/\*\s*,/
-
-
1
def valid_accept_header
-
1
(xhr? && (accept || content_mime_type)) ||
-
2
(accept && accept !~ BROWSER_LIKE_ACCEPTS)
-
end
-
-
1
def use_accept_header
-
1
!self.class.ignore_accept_header
-
end
-
end
-
end
-
end
-
1
require 'set'
-
1
require 'active_support/core_ext/class/attribute_accessors'
-
1
require 'active_support/core_ext/string/starts_ends_with'
-
-
1
module Mime
-
1
class Mimes < Array
-
1
def symbols
-
22
@symbols ||= map { |m| m.to_sym }
-
end
-
-
1
%w(<< concat shift unshift push pop []= clear compact! collect!
-
delete delete_at delete_if flatten! map! insert reject! reverse!
-
replace slice! sort! uniq!).each do |method|
-
22
module_eval <<-CODE, __FILE__, __LINE__ + 1
-
def #{method}(*)
-
@symbols = nil
-
super
-
end
-
CODE
-
end
-
end
-
-
1
SET = Mimes.new
-
1
EXTENSION_LOOKUP = {}
-
1
LOOKUP = Hash.new { |h, k| h[k] = Type.new(k) unless k.blank? }
-
-
1
class << self
-
1
def [](type)
-
82
return type if type.is_a?(Type)
-
82
Type.lookup_by_extension(type)
-
end
-
-
1
def fetch(type)
-
return type if type.is_a?(Type)
-
EXTENSION_LOOKUP.fetch(type.to_s) { |k| yield k }
-
end
-
end
-
-
# Encapsulates the notion of a mime type. Can be used at render time, for example, with:
-
#
-
# class PostsController < ActionController::Base
-
# def show
-
# @post = Post.find(params[:id])
-
#
-
# respond_to do |format|
-
# format.html
-
# format.ics { render text: post.to_ics, mime_type: Mime::Type["text/calendar"] }
-
# format.xml { render xml: @people }
-
# end
-
# end
-
# end
-
1
class Type
-
1
@@html_types = Set.new [:html, :all]
-
1
cattr_reader :html_types
-
-
# These are the content types which browsers can generate without using ajax, flash, etc
-
# i.e. following a link, getting an image or posting a form. CSRF protection
-
# only needs to protect against these types.
-
1
@@browser_generated_types = Set.new [:html, :url_encoded_form, :multipart_form, :text]
-
1
attr_reader :symbol
-
-
1
@register_callbacks = []
-
-
# A simple helper class used in parsing the accept header
-
1
class AcceptItem #:nodoc:
-
1
attr_accessor :index, :name, :q
-
1
alias :to_s :name
-
-
1
def initialize(index, name, q = nil)
-
@index = index
-
@name = name
-
q ||= 0.0 if @name == Mime::ALL.to_s # default wildcard match to end of list
-
@q = ((q || 1.0).to_f * 100).to_i
-
end
-
-
1
def <=>(item)
-
result = item.q <=> @q
-
result = @index <=> item.index if result == 0
-
result
-
end
-
-
1
def ==(item)
-
@name == item.to_s
-
end
-
end
-
-
1
class AcceptList < Array #:nodoc:
-
1
def assort!
-
sort!
-
-
# Take care of the broken text/xml entry by renaming or deleting it
-
if text_xml_idx && app_xml_idx
-
app_xml.q = [text_xml.q, app_xml.q].max # set the q value to the max of the two
-
exchange_xml_items if app_xml_idx > text_xml_idx # make sure app_xml is ahead of text_xml in the list
-
delete_at(text_xml_idx) # delete text_xml from the list
-
elsif text_xml_idx
-
text_xml.name = Mime::XML.to_s
-
end
-
-
# Look for more specific XML-based types and sort them ahead of app/xml
-
if app_xml_idx
-
idx = app_xml_idx
-
-
while idx < length
-
type = self[idx]
-
break if type.q < app_xml.q
-
-
if type.name.ends_with? '+xml'
-
self[app_xml_idx], self[idx] = self[idx], app_xml
-
@app_xml_idx = idx
-
end
-
idx += 1
-
end
-
end
-
-
map! { |i| Mime::Type.lookup(i.name) }.uniq!
-
to_a
-
end
-
-
1
private
-
1
def text_xml_idx
-
@text_xml_idx ||= index('text/xml')
-
end
-
-
1
def app_xml_idx
-
@app_xml_idx ||= index(Mime::XML.to_s)
-
end
-
-
1
def text_xml
-
self[text_xml_idx]
-
end
-
-
1
def app_xml
-
self[app_xml_idx]
-
end
-
-
1
def exchange_xml_items
-
self[app_xml_idx], self[text_xml_idx] = text_xml, app_xml
-
@app_xml_idx, @text_xml_idx = text_xml_idx, app_xml_idx
-
end
-
end
-
-
1
class << self
-
1
TRAILING_STAR_REGEXP = /(text|application)\/\*/
-
1
PARAMETER_SEPARATOR_REGEXP = /;\s*\w+="?\w+"?/
-
-
1
def register_callback(&block)
-
1
@register_callbacks << block
-
end
-
-
1
def lookup(string)
-
2
LOOKUP[string]
-
end
-
-
1
def lookup_by_extension(extension)
-
82
EXTENSION_LOOKUP[extension.to_s]
-
end
-
-
# Registers an alias that's not used on mime type lookup, but can be referenced directly. Especially useful for
-
# rendering different HTML versions depending on the user agent, like an iPhone.
-
1
def register_alias(string, symbol, extension_synonyms = [])
-
register(string, symbol, [], extension_synonyms, true)
-
end
-
-
1
def register(string, symbol, mime_type_synonyms = [], extension_synonyms = [], skip_lookup = false)
-
21
Mime.const_set(symbol.upcase, Type.new(string, symbol, mime_type_synonyms))
-
-
21
new_mime = Mime.const_get(symbol.upcase)
-
21
SET << new_mime
-
-
50
([string] + mime_type_synonyms).each { |str| LOOKUP[str] = SET.last } unless skip_lookup
-
58
([symbol] + extension_synonyms).each { |ext| EXTENSION_LOOKUP[ext.to_s] = SET.last }
-
-
21
@register_callbacks.each do |callback|
-
callback.call(new_mime)
-
end
-
end
-
-
1
def parse(accept_header)
-
if accept_header !~ /,/
-
accept_header = accept_header.split(PARAMETER_SEPARATOR_REGEXP).first
-
parse_trailing_star(accept_header) || [Mime::Type.lookup(accept_header)]
-
else
-
list, index = AcceptList.new, 0
-
accept_header.split(',').each do |header|
-
params, q = header.split(PARAMETER_SEPARATOR_REGEXP)
-
if params.present?
-
params.strip!
-
-
params = parse_trailing_star(params) || [params]
-
-
params.each do |m|
-
list << AcceptItem.new(index, m.to_s, q)
-
index += 1
-
end
-
end
-
end
-
list.assort!
-
end
-
end
-
-
1
def parse_trailing_star(accept_header)
-
parse_data_with_trailing_star($1) if accept_header =~ TRAILING_STAR_REGEXP
-
end
-
-
# For an input of <tt>'text'</tt>, returns <tt>[Mime::JSON, Mime::XML, Mime::ICS,
-
# Mime::HTML, Mime::CSS, Mime::CSV, Mime::JS, Mime::YAML, Mime::TEXT]</tt>.
-
#
-
# For an input of <tt>'application'</tt>, returns <tt>[Mime::HTML, Mime::JS,
-
# Mime::XML, Mime::YAML, Mime::ATOM, Mime::JSON, Mime::RSS, Mime::URL_ENCODED_FORM]</tt>.
-
1
def parse_data_with_trailing_star(input)
-
Mime::SET.select { |m| m =~ input }
-
end
-
-
# This method is opposite of register method.
-
#
-
# Usage:
-
#
-
# Mime::Type.unregister(:mobile)
-
1
def unregister(symbol)
-
symbol = symbol.upcase
-
mime = Mime.const_get(symbol)
-
Mime.instance_eval { remove_const(symbol) }
-
-
SET.delete_if { |v| v.eql?(mime) }
-
LOOKUP.delete_if { |k,v| v.eql?(mime) }
-
EXTENSION_LOOKUP.delete_if { |k,v| v.eql?(mime) }
-
end
-
end
-
-
1
def initialize(string, symbol = nil, synonyms = [])
-
22
@symbol, @synonyms = symbol, synonyms
-
22
@string = string
-
end
-
-
1
def to_s
-
124
@string
-
end
-
-
1
def to_str
-
to_s
-
end
-
-
1
def to_sym
-
135
@symbol
-
end
-
-
1
def ref
-
40
to_sym || to_s
-
end
-
-
1
def ===(list)
-
if list.is_a?(Array)
-
(@synonyms + [ self ]).any? { |synonym| list.include?(synonym) }
-
else
-
super
-
end
-
end
-
-
1
def ==(mime_type)
-
return false if mime_type.blank?
-
(@synonyms + [ self ]).any? do |synonym|
-
synonym.to_s == mime_type.to_s || synonym.to_sym == mime_type.to_sym
-
end
-
end
-
-
1
def =~(mime_type)
-
return false if mime_type.blank?
-
regexp = Regexp.new(Regexp.quote(mime_type.to_s))
-
(@synonyms + [ self ]).any? do |synonym|
-
synonym.to_s =~ regexp
-
end
-
end
-
-
# Returns true if Action Pack should check requests using this Mime Type for possible request forgery. See
-
# ActionController::RequestForgeryProtection.
-
1
def verify_request?
-
ActiveSupport::Deprecation.warn "Mime::Type#verify_request? is deprecated and will be removed in Rails 4.1"
-
@@browser_generated_types.include?(to_sym)
-
end
-
-
1
def self.browser_generated_types
-
ActiveSupport::Deprecation.warn "Mime::Type.browser_generated_types is deprecated and will be removed in Rails 4.1"
-
@@browser_generated_types
-
end
-
-
1
def html?
-
@@html_types.include?(to_sym) || @string =~ /html/
-
end
-
-
-
1
private
-
-
1
def to_ary; end
-
1
def to_a; end
-
-
1
def method_missing(method, *args)
-
if method.to_s.ends_with? '?'
-
method[0..-2].downcase.to_sym == to_sym
-
else
-
super
-
end
-
end
-
-
1
def respond_to_missing?(method, include_private = false) #:nodoc:
-
method.to_s.ends_with? '?'
-
end
-
end
-
end
-
-
1
require 'action_dispatch/http/mime_types'
-
# Build list of Mime types for HTTP responses
-
# http://www.iana.org/assignments/media-types/
-
-
1
Mime::Type.register "text/html", :html, %w( application/xhtml+xml ), %w( xhtml )
-
1
Mime::Type.register "text/plain", :text, [], %w(txt)
-
1
Mime::Type.register "text/javascript", :js, %w( application/javascript application/x-javascript )
-
1
Mime::Type.register "text/css", :css
-
1
Mime::Type.register "text/calendar", :ics
-
1
Mime::Type.register "text/csv", :csv
-
-
1
Mime::Type.register "image/png", :png, [], %w(png)
-
1
Mime::Type.register "image/jpeg", :jpeg, [], %w(jpg jpeg jpe pjpeg)
-
1
Mime::Type.register "image/gif", :gif, [], %w(gif)
-
1
Mime::Type.register "image/bmp", :bmp, [], %w(bmp)
-
1
Mime::Type.register "image/tiff", :tiff, [], %w(tif tiff)
-
-
1
Mime::Type.register "video/mpeg", :mpeg, [], %w(mpg mpeg mpe)
-
-
1
Mime::Type.register "application/xml", :xml, %w( text/xml application/x-xml )
-
1
Mime::Type.register "application/rss+xml", :rss
-
1
Mime::Type.register "application/atom+xml", :atom
-
1
Mime::Type.register "application/x-yaml", :yaml, %w( text/yaml )
-
-
1
Mime::Type.register "multipart/form-data", :multipart_form
-
1
Mime::Type.register "application/x-www-form-urlencoded", :url_encoded_form
-
-
# http://www.ietf.org/rfc/rfc4627.txt
-
# http://www.json.org/JSONRequest.html
-
1
Mime::Type.register "application/json", :json, %w( text/x-json application/jsonrequest )
-
-
1
Mime::Type.register "application/pdf", :pdf, [], %w(pdf)
-
1
Mime::Type.register "application/zip", :zip, [], %w(zip)
-
-
# Create Mime::ALL but do not add it to the SET.
-
1
Mime::ALL = Mime::Type.new("*/*", :all, [])
-
1
module ActionDispatch
-
1
module Http
-
1
class ParameterFilter
-
1
FILTERED = '[FILTERED]'.freeze # :nodoc:
-
-
1
def initialize(filters = [])
-
2
@filters = filters
-
end
-
-
1
def filter(params)
-
1
compiled_filter.call(params)
-
end
-
-
1
private
-
-
1
def compiled_filter
-
1
@compiled_filter ||= CompiledFilter.compile(@filters)
-
end
-
-
1
class CompiledFilter # :nodoc:
-
1
def self.compile(filters)
-
2
return lambda { |params| params.dup } if filters.empty?
-
-
strings, regexps, blocks = [], [], []
-
-
filters.each do |item|
-
case item
-
when Proc
-
blocks << item
-
when Regexp
-
regexps << item
-
else
-
strings << item.to_s
-
end
-
end
-
-
regexps << Regexp.new(strings.join('|'), true) unless strings.empty?
-
new regexps, blocks
-
end
-
-
1
attr_reader :regexps, :blocks
-
-
1
def initialize(regexps, blocks)
-
@regexps = regexps
-
@blocks = blocks
-
end
-
-
1
def call(original_params)
-
filtered_params = {}
-
-
original_params.each do |key, value|
-
if regexps.any? { |r| key =~ r }
-
value = FILTERED
-
elsif value.is_a?(Hash)
-
value = call(value)
-
elsif value.is_a?(Array)
-
value = value.map { |v| v.is_a?(Hash) ? call(v) : v }
-
elsif blocks.any?
-
key = key.dup
-
value = value.dup if value.duplicable?
-
blocks.each { |b| b.call(key, value) }
-
end
-
-
filtered_params[key] = value
-
end
-
-
filtered_params
-
end
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/hash/keys'
-
1
require 'active_support/core_ext/hash/indifferent_access'
-
-
1
module ActionDispatch
-
1
module Http
-
1
module Parameters
-
1
def initialize(env)
-
3
super
-
3
@symbolized_path_params = nil
-
end
-
-
# Returns both GET and POST \parameters in a single hash.
-
1
def parameters
-
2
@env["action_dispatch.request.parameters"] ||= begin
-
1
params = request_parameters.merge(query_parameters)
-
1
params.merge!(path_parameters)
-
1
encode_params(params).with_indifferent_access
-
end
-
end
-
1
alias :params :parameters
-
-
1
def path_parameters=(parameters) #:nodoc:
-
@symbolized_path_params = nil
-
@env.delete("action_dispatch.request.parameters")
-
@env["action_dispatch.request.path_parameters"] = parameters
-
end
-
-
# The same as <tt>path_parameters</tt> with explicitly symbolized keys.
-
1
def symbolized_path_parameters
-
@symbolized_path_params ||= path_parameters.symbolize_keys
-
end
-
-
# Returns a hash with the \parameters used to form the \path of the request.
-
# Returned hash keys are strings:
-
#
-
# {'action' => 'my_action', 'controller' => 'my_controller'}
-
#
-
# See <tt>symbolized_path_parameters</tt> for symbolized keys.
-
1
def path_parameters
-
1
@env["action_dispatch.request.path_parameters"] ||= {}
-
end
-
-
1
def reset_parameters #:nodoc:
-
@env.delete("action_dispatch.request.parameters")
-
end
-
-
1
private
-
-
# TODO: Validate that the characters are UTF-8. If they aren't,
-
# you'll get a weird error down the road, but our form handling
-
# should really prevent that from happening
-
1
def encode_params(params)
-
3
if params.is_a?(String)
-
2
return params.force_encoding("UTF-8").encode!
-
elsif !params.is_a?(Hash)
-
return params
-
end
-
-
1
params.each do |k, v|
-
2
case v
-
when Hash
-
encode_params(v)
-
when Array
-
v.map! {|el| encode_params(el) }
-
else
-
2
encode_params(v)
-
end
-
end
-
end
-
-
# Convert nested Hash to HashWithIndifferentAccess
-
1
def normalize_parameters(value)
-
2
case value
-
when Hash
-
2
h = {}
-
2
value.each { |k, v| h[k] = normalize_parameters(v) }
-
2
h.with_indifferent_access
-
when Array
-
value.map { |e| normalize_parameters(e) }
-
else
-
value
-
end
-
end
-
end
-
end
-
end
-
1
require 'tempfile'
-
1
require 'stringio'
-
1
require 'strscan'
-
-
1
require 'active_support/core_ext/hash/indifferent_access'
-
1
require 'active_support/core_ext/string/access'
-
1
require 'active_support/inflector'
-
1
require 'action_dispatch/http/headers'
-
1
require 'action_controller/metal/exceptions'
-
-
1
module ActionDispatch
-
1
class Request < Rack::Request
-
1
include ActionDispatch::Http::Cache::Request
-
1
include ActionDispatch::Http::MimeNegotiation
-
1
include ActionDispatch::Http::Parameters
-
1
include ActionDispatch::Http::FilterParameters
-
1
include ActionDispatch::Http::Upload
-
1
include ActionDispatch::Http::URL
-
-
1
autoload :Session, 'action_dispatch/request/session'
-
-
1
LOCALHOST = Regexp.union [/^127\.0\.0\.\d{1,3}$/, /^::1$/, /^0:0:0:0:0:0:0:1(%.*)?$/]
-
-
1
ENV_METHODS = %w[ AUTH_TYPE GATEWAY_INTERFACE
-
PATH_TRANSLATED REMOTE_HOST
-
REMOTE_IDENT REMOTE_USER REMOTE_ADDR
-
SERVER_NAME SERVER_PROTOCOL
-
-
HTTP_ACCEPT HTTP_ACCEPT_CHARSET HTTP_ACCEPT_ENCODING
-
HTTP_ACCEPT_LANGUAGE HTTP_CACHE_CONTROL HTTP_FROM
-
HTTP_NEGOTIATE HTTP_PRAGMA ].freeze
-
-
1
ENV_METHODS.each do |env|
-
17
class_eval <<-METHOD, __FILE__, __LINE__ + 1
-
def #{env.sub(/^HTTP_/n, '').downcase} # def accept_charset
-
@env["#{env}"] # @env["HTTP_ACCEPT_CHARSET"]
-
end # end
-
METHOD
-
end
-
-
1
def initialize(env)
-
3
super
-
3
@method = nil
-
3
@request_method = nil
-
3
@remote_ip = nil
-
3
@original_fullpath = nil
-
3
@fullpath = nil
-
3
@ip = nil
-
3
@uuid = nil
-
end
-
-
1
def key?(key)
-
@env.key?(key)
-
end
-
-
# List of HTTP request methods from the following RFCs:
-
# Hypertext Transfer Protocol -- HTTP/1.1 (http://www.ietf.org/rfc/rfc2616.txt)
-
# HTTP Extensions for Distributed Authoring -- WEBDAV (http://www.ietf.org/rfc/rfc2518.txt)
-
# Versioning Extensions to WebDAV (http://www.ietf.org/rfc/rfc3253.txt)
-
# Ordered Collections Protocol (WebDAV) (http://www.ietf.org/rfc/rfc3648.txt)
-
# Web Distributed Authoring and Versioning (WebDAV) Access Control Protocol (http://www.ietf.org/rfc/rfc3744.txt)
-
# Web Distributed Authoring and Versioning (WebDAV) SEARCH (http://www.ietf.org/rfc/rfc5323.txt)
-
# PATCH Method for HTTP (http://www.ietf.org/rfc/rfc5789.txt)
-
1
RFC2616 = %w(OPTIONS GET HEAD POST PUT DELETE TRACE CONNECT)
-
1
RFC2518 = %w(PROPFIND PROPPATCH MKCOL COPY MOVE LOCK UNLOCK)
-
1
RFC3253 = %w(VERSION-CONTROL REPORT CHECKOUT CHECKIN UNCHECKOUT MKWORKSPACE UPDATE LABEL MERGE BASELINE-CONTROL MKACTIVITY)
-
1
RFC3648 = %w(ORDERPATCH)
-
1
RFC3744 = %w(ACL)
-
1
RFC5323 = %w(SEARCH)
-
1
RFC5789 = %w(PATCH)
-
-
1
HTTP_METHODS = RFC2616 + RFC2518 + RFC3253 + RFC3648 + RFC3744 + RFC5323 + RFC5789
-
-
1
HTTP_METHOD_LOOKUP = {}
-
-
# Populate the HTTP method lookup cache
-
1
HTTP_METHODS.each { |method|
-
30
HTTP_METHOD_LOOKUP[method] = method.underscore.to_sym
-
}
-
-
# Returns the HTTP \method that the application should see.
-
# In the case where the \method was overridden by a middleware
-
# (for instance, if a HEAD request was converted to a GET,
-
# or if a _method parameter was used to determine the \method
-
# the application should use), this \method returns the overridden
-
# value, not the original.
-
1
def request_method
-
3
@request_method ||= check_method(env["REQUEST_METHOD"])
-
end
-
-
# Returns a symbol form of the #request_method
-
1
def request_method_symbol
-
HTTP_METHOD_LOOKUP[request_method]
-
end
-
-
# Returns the original value of the environment's REQUEST_METHOD,
-
# even if it was overridden by middleware. See #request_method for
-
# more information.
-
1
def method
-
1
@method ||= check_method(env["rack.methodoverride.original_method"] || env['REQUEST_METHOD'])
-
end
-
-
# Returns a symbol form of the #method
-
1
def method_symbol
-
HTTP_METHOD_LOOKUP[method]
-
end
-
-
# Is this a GET (or HEAD) request?
-
# Equivalent to <tt>request.request_method_symbol == :get</tt>.
-
1
def get?
-
HTTP_METHOD_LOOKUP[request_method] == :get
-
end
-
-
# Is this a POST request?
-
# Equivalent to <tt>request.request_method_symbol == :post</tt>.
-
1
def post?
-
HTTP_METHOD_LOOKUP[request_method] == :post
-
end
-
-
# Is this a PATCH request?
-
# Equivalent to <tt>request.request_method == :patch</tt>.
-
1
def patch?
-
HTTP_METHOD_LOOKUP[request_method] == :patch
-
end
-
-
# Is this a PUT request?
-
# Equivalent to <tt>request.request_method_symbol == :put</tt>.
-
1
def put?
-
HTTP_METHOD_LOOKUP[request_method] == :put
-
end
-
-
# Is this a DELETE request?
-
# Equivalent to <tt>request.request_method_symbol == :delete</tt>.
-
1
def delete?
-
HTTP_METHOD_LOOKUP[request_method] == :delete
-
end
-
-
# Is this a HEAD request?
-
# Equivalent to <tt>request.request_method_symbol == :head</tt>.
-
1
def head?
-
HTTP_METHOD_LOOKUP[request_method] == :head
-
end
-
-
# Provides access to the request's HTTP headers, for example:
-
#
-
# request.headers["Content-Type"] # => "text/plain"
-
1
def headers
-
Http::Headers.new(@env)
-
end
-
-
1
def original_fullpath
-
@original_fullpath ||= (env["ORIGINAL_FULLPATH"] || fullpath)
-
end
-
-
1
def fullpath
-
1
@fullpath ||= super
-
end
-
-
1
def original_url
-
base_url + original_fullpath
-
end
-
-
1
def media_type
-
content_mime_type.to_s
-
end
-
-
# Returns the content length of the request as an integer.
-
1
def content_length
-
super.to_i
-
end
-
-
# Returns true if the "X-Requested-With" header contains "XMLHttpRequest"
-
# (case-insensitive). All major JavaScript libraries send this header with
-
# every Ajax request.
-
1
def xml_http_request?
-
2
@env['HTTP_X_REQUESTED_WITH'] =~ /XMLHttpRequest/i
-
end
-
1
alias :xhr? :xml_http_request?
-
-
1
def ip
-
2
@ip ||= super
-
end
-
-
# Originating IP address, usually set by the RemoteIp middleware.
-
1
def remote_ip
-
@remote_ip ||= (@env["action_dispatch.remote_ip"] || ip).to_s
-
end
-
-
# Returns the unique request id, which is based off either the X-Request-Id header that can
-
# be generated by a firewall, load balancer, or web server or by the RequestId middleware
-
# (which sets the action_dispatch.request_id environment variable).
-
#
-
# This unique ID is useful for tracing a request from end-to-end as part of logging or debugging.
-
# This relies on the rack variable set by the ActionDispatch::RequestId middleware.
-
1
def uuid
-
@uuid ||= env["action_dispatch.request_id"]
-
end
-
-
# Returns the lowercase name of the HTTP server software.
-
1
def server_software
-
(@env['SERVER_SOFTWARE'] && /^([a-zA-Z]+)/ =~ @env['SERVER_SOFTWARE']) ? $1.downcase : nil
-
end
-
-
# Read the request \body. This is useful for web services that need to
-
# work with raw requests directly.
-
1
def raw_post
-
unless @env.include? 'RAW_POST_DATA'
-
@env['RAW_POST_DATA'] = body.read(@env['CONTENT_LENGTH'].to_i)
-
body.rewind if body.respond_to?(:rewind)
-
end
-
@env['RAW_POST_DATA']
-
end
-
-
# The request body is an IO input stream. If the RAW_POST_DATA environment
-
# variable is already set, wrap it in a StringIO.
-
1
def body
-
if raw_post = @env['RAW_POST_DATA']
-
raw_post.force_encoding(Encoding::BINARY)
-
StringIO.new(raw_post)
-
else
-
@env['rack.input']
-
end
-
end
-
-
1
def form_data?
-
1
FORM_DATA_MEDIA_TYPES.include?(content_mime_type.to_s)
-
end
-
-
1
def body_stream #:nodoc:
-
@env['rack.input']
-
end
-
-
# TODO This should be broken apart into AD::Request::Session and probably
-
# be included by the session middleware.
-
1
def reset_session
-
if session && session.respond_to?(:destroy)
-
session.destroy
-
else
-
self.session = {}
-
end
-
@env['action_dispatch.request.flash_hash'] = nil
-
end
-
-
1
def session=(session) #:nodoc:
-
Session.set @env, session
-
end
-
-
1
def session_options=(options)
-
Session::Options.set @env, options
-
end
-
-
# Override Rack's GET method to support indifferent access
-
1
def GET
-
1
@env["action_dispatch.request.query_parameters"] ||= (normalize_parameters(super) || {})
-
rescue TypeError => e
-
raise ActionController::BadRequest.new(:query, e)
-
end
-
1
alias :query_parameters :GET
-
-
# Override Rack's POST method to support indifferent access
-
1
def POST
-
1
@env["action_dispatch.request.request_parameters"] ||= (normalize_parameters(super) || {})
-
rescue TypeError => e
-
raise ActionController::BadRequest.new(:request, e)
-
end
-
1
alias :request_parameters :POST
-
-
# Returns the authorization header regardless of whether it was specified directly or through one of the
-
# proxy alternatives.
-
1
def authorization
-
@env['HTTP_AUTHORIZATION'] ||
-
@env['X-HTTP_AUTHORIZATION'] ||
-
@env['X_HTTP_AUTHORIZATION'] ||
-
@env['REDIRECT_X_HTTP_AUTHORIZATION']
-
end
-
-
# True if the request came from localhost, 127.0.0.1.
-
1
def local?
-
LOCALHOST =~ remote_addr && LOCALHOST =~ remote_ip
-
end
-
-
1
protected
-
-
# Remove nils from the params hash
-
1
def deep_munge(hash)
-
2
hash.each_value do |v|
-
case v
-
when Array
-
v.grep(Hash) { |x| deep_munge(x) }
-
v.compact!
-
when Hash
-
deep_munge(v)
-
end
-
end
-
-
2
hash
-
end
-
-
1
def parse_query(qs)
-
2
deep_munge(super)
-
end
-
-
1
private
-
-
1
def check_method(name)
-
2
HTTP_METHOD_LOOKUP[name] || raise(ActionController::UnknownHttpMethod, "#{name}, accepted HTTP methods are #{HTTP_METHODS.to_sentence(:locale => :en)}")
-
2
name
-
end
-
end
-
end
-
1
require 'digest/md5'
-
1
require 'active_support/core_ext/class/attribute_accessors'
-
1
require 'monitor'
-
-
1
module ActionDispatch # :nodoc:
-
# Represents an HTTP response generated by a controller action. Use it to
-
# retrieve the current state of the response, or customize the response. It can
-
# either represent a real HTTP response (i.e. one that is meant to be sent
-
# back to the web browser) or a TestResponse (i.e. one that is generated
-
# from integration tests).
-
#
-
# \Response is mostly a Ruby on \Rails framework implementation detail, and
-
# should never be used directly in controllers. Controllers should use the
-
# methods defined in ActionController::Base instead. For example, if you want
-
# to set the HTTP response's content MIME type, then use
-
# ActionControllerBase#headers instead of Response#headers.
-
#
-
# Nevertheless, integration tests may want to inspect controller responses in
-
# more detail, and that's when \Response can be useful for application
-
# developers. Integration test methods such as
-
# ActionDispatch::Integration::Session#get and
-
# ActionDispatch::Integration::Session#post return objects of type
-
# TestResponse (which are of course also of type \Response).
-
#
-
# For example, the following demo integration test prints the body of the
-
# controller response to the console:
-
#
-
# class DemoControllerTest < ActionDispatch::IntegrationTest
-
# def test_print_root_path_to_console
-
# get('/')
-
# puts response.body
-
# end
-
# end
-
1
class Response
-
1
attr_accessor :request, :header
-
1
attr_reader :status
-
1
attr_writer :sending_file
-
-
1
alias_method :headers=, :header=
-
1
alias_method :headers, :header
-
-
1
delegate :[], :[]=, :to => :@header
-
1
delegate :each, :to => :@stream
-
-
# Sets the HTTP response's content MIME type. For example, in the controller
-
# you could write this:
-
#
-
# response.content_type = "text/plain"
-
#
-
# If a character set has been defined for this response (see charset=) then
-
# the character set information will also be included in the content type
-
# information.
-
1
attr_accessor :charset
-
1
attr_reader :content_type
-
-
1
CONTENT_TYPE = "Content-Type".freeze
-
1
SET_COOKIE = "Set-Cookie".freeze
-
1
LOCATION = "Location".freeze
-
-
2
cattr_accessor(:default_charset) { "utf-8" }
-
1
cattr_accessor(:default_headers)
-
-
1
include Rack::Response::Helpers
-
1
include ActionDispatch::Http::Cache::Response
-
1
include MonitorMixin
-
-
1
class Buffer # :nodoc:
-
1
def initialize(response, buf)
-
3
@response = response
-
3
@buf = buf
-
3
@closed = false
-
end
-
-
1
def write(string)
-
raise IOError, "closed stream" if closed?
-
-
@response.commit!
-
@buf.push string
-
end
-
-
1
def each(&block)
-
2
@buf.each(&block)
-
end
-
-
1
def close
-
1
@response.commit!
-
1
@closed = true
-
end
-
-
1
def closed?
-
@closed
-
end
-
end
-
-
1
attr_reader :stream
-
-
1
def initialize(status = 200, header = {}, body = [])
-
2
super()
-
-
2
header = merge_default_headers(header, self.class.default_headers)
-
-
2
self.body, self.header, self.status = body, header, status
-
-
2
@sending_file = false
-
2
@blank = false
-
2
@cv = new_cond
-
2
@committed = false
-
2
@content_type = nil
-
2
@charset = nil
-
-
2
if content_type = self[CONTENT_TYPE]
-
1
type, charset = content_type.split(/;\s*charset=/)
-
1
@content_type = Mime::Type.lookup(type)
-
1
@charset = charset || self.class.default_charset
-
end
-
-
2
prepare_cache_control!
-
-
2
yield self if block_given?
-
end
-
-
1
def await_commit
-
synchronize do
-
@cv.wait_until { @committed }
-
end
-
end
-
-
1
def commit!
-
1
synchronize do
-
1
@committed = true
-
1
@cv.broadcast
-
end
-
end
-
-
1
def committed?
-
@committed
-
end
-
-
1
def status=(status)
-
2
@status = Rack::Utils.status_code(status)
-
end
-
-
1
def content_type=(content_type)
-
1
@content_type = content_type.to_s
-
end
-
-
# The response code of the request
-
1
def response_code
-
@status
-
end
-
-
# Returns a String to ensure compatibility with Net::HTTPResponse
-
1
def code
-
@status.to_s
-
end
-
-
1
def message
-
Rack::Utils::HTTP_STATUS_CODES[@status]
-
end
-
1
alias_method :status_message, :message
-
-
1
def respond_to?(method)
-
3
if method.to_sym == :to_path
-
stream.respond_to?(:to_path)
-
else
-
3
super
-
end
-
end
-
-
1
def to_path
-
stream.to_path
-
end
-
-
1
def body
-
1
strings = []
-
2
each { |part| strings << part.to_s }
-
1
strings.join
-
end
-
-
1
EMPTY = " "
-
-
1
def body=(body)
-
3
@blank = true if body == EMPTY
-
-
3
if body.respond_to?(:to_path)
-
@stream = body
-
else
-
3
@stream = build_buffer self, munge_body_object(body)
-
end
-
end
-
-
1
def body_parts
-
parts = []
-
@stream.each { |x| parts << x }
-
parts
-
end
-
-
1
def set_cookie(key, value)
-
::Rack::Utils.set_cookie_header!(header, key, value)
-
end
-
-
1
def delete_cookie(key, value={})
-
::Rack::Utils.delete_cookie_header!(header, key, value)
-
end
-
-
1
def location
-
headers[LOCATION]
-
end
-
1
alias_method :redirect_url, :location
-
-
1
def location=(url)
-
headers[LOCATION] = url
-
end
-
-
1
def close
-
1
stream.close if stream.respond_to?(:close)
-
end
-
-
1
def to_a
-
1
rack_response @status, @header.to_hash
-
end
-
1
alias prepare! to_a
-
1
alias to_ary to_a # For implicit splat on 1.9.2
-
-
# Returns the response cookies, converted to a Hash of (name => value) pairs
-
#
-
# assert_equal 'AuthorOfNewPage', r.cookies['author']
-
1
def cookies
-
cookies = {}
-
if header = self[SET_COOKIE]
-
header = header.split("\n") if header.respond_to?(:to_str)
-
header.each do |cookie|
-
if pair = cookie.split(';').first
-
key, value = pair.split("=").map { |v| Rack::Utils.unescape(v) }
-
cookies[key] = value
-
end
-
end
-
end
-
cookies
-
end
-
-
1
private
-
-
1
def merge_default_headers(original, default)
-
2
return original unless default.respond_to?(:merge)
-
-
default.merge(original)
-
end
-
-
1
def build_buffer(response, body)
-
3
Buffer.new response, body
-
end
-
-
1
def munge_body_object(body)
-
3
body.respond_to?(:each) ? body : [body]
-
end
-
-
1
def assign_default_content_type_and_charset!(headers)
-
1
return if headers[CONTENT_TYPE].present?
-
-
1
@content_type ||= Mime::HTML
-
1
@charset ||= self.class.default_charset
-
-
1
type = @content_type.to_s.dup
-
1
type << "; charset=#{@charset}" unless @sending_file
-
-
1
headers[CONTENT_TYPE] = type
-
end
-
-
1
def rack_response(status, header)
-
1
assign_default_content_type_and_charset!(header)
-
1
handle_conditional_get!
-
-
1
header[SET_COOKIE] = header[SET_COOKIE].join("\n") if header[SET_COOKIE].respond_to?(:join)
-
-
1
if [204, 304].include?(@status)
-
header.delete CONTENT_TYPE
-
[status, header, []]
-
else
-
1
[status, header, self]
-
end
-
end
-
end
-
end
-
1
module ActionDispatch
-
1
module Http
-
# Models uploaded files.
-
#
-
# The actual file is accessible via the +tempfile+ accessor, though some
-
# of its interface is available directly for convenience.
-
#
-
# Uploaded files are temporary files whose lifespan is one request. When
-
# the object is finalized Ruby unlinks the file, so there is not need to
-
# clean them with a separate maintenance task.
-
1
class UploadedFile
-
# The basename of the file in the client.
-
1
attr_accessor :original_filename
-
-
# A string with the MIME type of the file.
-
1
attr_accessor :content_type
-
-
# A +Tempfile+ object with the actual uploaded file. Note that some of
-
# its interface is available directly.
-
1
attr_accessor :tempfile
-
-
# TODO.
-
1
attr_accessor :headers
-
-
1
def initialize(hash) # :nodoc:
-
@tempfile = hash[:tempfile]
-
raise(ArgumentError, ':tempfile is required') unless @tempfile
-
-
@original_filename = encode_filename(hash[:filename])
-
@content_type = hash[:type]
-
@headers = hash[:head]
-
end
-
-
# Shortcut for +tempfile.read+.
-
1
def read(length=nil, buffer=nil)
-
@tempfile.read(length, buffer)
-
end
-
-
# Shortcut for +tempfile.open+.
-
1
def open
-
@tempfile.open
-
end
-
-
# Shortcut for +tempfile.close+.
-
1
def close(unlink_now=false)
-
@tempfile.close(unlink_now)
-
end
-
-
# Shortcut for +tempfile.path+.
-
1
def path
-
@tempfile.path
-
end
-
-
# Shortcut for +tempfile.rewind+.
-
1
def rewind
-
@tempfile.rewind
-
end
-
-
# Shortcut for +tempfile.size+.
-
1
def size
-
@tempfile.size
-
end
-
-
# Shortcut for +tempfile.eof?+.
-
1
def eof?
-
@tempfile.eof?
-
end
-
-
1
private
-
-
1
def encode_filename(filename)
-
# Encode the filename in the utf8 encoding, unless it is nil
-
filename.force_encoding("UTF-8").encode! if filename
-
end
-
end
-
-
1
module Upload # :nodoc:
-
# Convert nested Hash to HashWithIndifferentAccess and replace
-
# file upload hash with UploadedFile objects
-
1
def normalize_parameters(value)
-
2
if Hash === value && value.has_key?(:tempfile)
-
UploadedFile.new(value)
-
else
-
2
super
-
end
-
end
-
1
private :normalize_parameters
-
end
-
end
-
end
-
1
module ActionDispatch
-
1
module Http
-
1
module URL
-
1
IP_HOST_REGEXP = /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/
-
-
1
mattr_accessor :tld_length
-
1
self.tld_length = 1
-
-
1
class << self
-
1
def extract_domain(host, tld_length = @@tld_length)
-
return nil unless named_host?(host)
-
host.split('.').last(1 + tld_length).join('.')
-
end
-
-
1
def extract_subdomains(host, tld_length = @@tld_length)
-
return [] unless named_host?(host)
-
parts = host.split('.')
-
parts[0..-(tld_length+2)]
-
end
-
-
1
def extract_subdomain(host, tld_length = @@tld_length)
-
extract_subdomains(host, tld_length).join('.')
-
end
-
-
1
def url_for(options = {})
-
4
path = ""
-
4
path << options.delete(:script_name).to_s.chomp("/")
-
4
path << options.delete(:path).to_s
-
-
4
params = options[:params] || {}
-
4
params.reject! {|k,v| v.to_param.nil? }
-
-
4
result = build_host_url(options)
-
-
4
result << (options[:trailing_slash] ? path.sub(/\?|\z/) { "/" + $& } : path)
-
4
result << "?#{params.to_query}" unless params.empty?
-
4
result << "##{Journey::Router::Utils.escape_fragment(options[:anchor].to_param.to_s)}" if options[:anchor]
-
4
result
-
end
-
-
1
private
-
-
1
def build_host_url(options)
-
4
if options[:host].blank? && options[:only_path].blank?
-
raise ArgumentError, 'Missing host to link to! Please provide the :host parameter, set default_url_options[:host], or set :only_path to true'
-
end
-
-
4
result = ""
-
-
4
unless options[:only_path]
-
4
unless options[:protocol] == false
-
4
result << (options[:protocol] || "http")
-
4
result << ":" unless result.match(%r{:|//})
-
end
-
4
result << "//" unless result.match("//")
-
4
result << rewrite_authentication(options)
-
4
result << host_or_subdomain_and_domain(options)
-
4
result << ":#{options.delete(:port)}" if options[:port]
-
end
-
4
result
-
end
-
-
1
def named_host?(host)
-
4
host && IP_HOST_REGEXP !~ host
-
end
-
-
1
def rewrite_authentication(options)
-
4
if options[:user] && options[:password]
-
"#{Rack::Utils.escape(options[:user])}:#{Rack::Utils.escape(options[:password])}@"
-
else
-
4
""
-
end
-
end
-
-
1
def host_or_subdomain_and_domain(options)
-
4
return options[:host] if !named_host?(options[:host]) || (options[:subdomain].nil? && options[:domain].nil?)
-
-
tld_length = options[:tld_length] || @@tld_length
-
-
host = ""
-
unless options[:subdomain] == false
-
host << (options[:subdomain] || extract_subdomain(options[:host], tld_length)).to_param
-
host << "."
-
end
-
host << (options[:domain] || extract_domain(options[:host], tld_length))
-
host
-
end
-
end
-
-
1
def initialize(env)
-
3
super
-
3
@protocol = nil
-
3
@port = nil
-
end
-
-
# Returns the complete URL used for this request.
-
1
def url
-
protocol + host_with_port + fullpath
-
end
-
-
# Returns 'https://' if this is an SSL request and 'http://' otherwise.
-
1
def protocol
-
@protocol ||= ssl? ? 'https://' : 'http://'
-
end
-
-
# Returns the \host for this request, such as "example.com".
-
1
def raw_host_with_port
-
if forwarded = env["HTTP_X_FORWARDED_HOST"]
-
forwarded.split(/,\s?/).last
-
else
-
env['HTTP_HOST'] || "#{env['SERVER_NAME'] || env['SERVER_ADDR']}:#{env['SERVER_PORT']}"
-
end
-
end
-
-
# Returns the host for this request, such as example.com.
-
1
def host
-
raw_host_with_port.sub(/:\d+$/, '')
-
end
-
-
# Returns a \host:\port string for this request, such as "example.com" or
-
# "example.com:8080".
-
1
def host_with_port
-
"#{host}#{port_string}"
-
end
-
-
# Returns the port number of this request as an integer.
-
1
def port
-
@port ||= begin
-
if raw_host_with_port =~ /:(\d+)$/
-
$1.to_i
-
else
-
standard_port
-
end
-
end
-
end
-
-
# Returns the standard \port number for this request's protocol.
-
1
def standard_port
-
case protocol
-
when 'https://' then 443
-
else 80
-
end
-
end
-
-
# Returns whether this request is using the standard port
-
1
def standard_port?
-
port == standard_port
-
end
-
-
# Returns a number \port suffix like 8080 if the \port number of this request
-
# is not the default HTTP \port 80 or HTTPS \port 443.
-
1
def optional_port
-
standard_port? ? nil : port
-
end
-
-
# Returns a string \port suffix, including colon, like ":8080" if the \port
-
# number of this request is not the default HTTP \port 80 or HTTPS \port 443.
-
1
def port_string
-
standard_port? ? '' : ":#{port}"
-
end
-
-
1
def server_port
-
@env['SERVER_PORT'].to_i
-
end
-
-
# Returns the \domain part of a \host, such as "rubyonrails.org" in "www.rubyonrails.org". You can specify
-
# a different <tt>tld_length</tt>, such as 2 to catch rubyonrails.co.uk in "www.rubyonrails.co.uk".
-
1
def domain(tld_length = @@tld_length)
-
ActionDispatch::Http::URL.extract_domain(host, tld_length)
-
end
-
-
# Returns all the \subdomains as an array, so <tt>["dev", "www"]</tt> would be
-
# returned for "dev.www.rubyonrails.org". You can specify a different <tt>tld_length</tt>,
-
# such as 2 to catch <tt>["www"]</tt> instead of <tt>["www", "rubyonrails"]</tt>
-
# in "www.rubyonrails.co.uk".
-
1
def subdomains(tld_length = @@tld_length)
-
ActionDispatch::Http::URL.extract_subdomains(host, tld_length)
-
end
-
-
# Returns all the \subdomains as a string, so <tt>"dev.www"</tt> would be
-
# returned for "dev.www.rubyonrails.org". You can specify a different <tt>tld_length</tt>,
-
# such as 2 to catch <tt>"www"</tt> instead of <tt>"www.rubyonrails"</tt>
-
# in "www.rubyonrails.co.uk".
-
1
def subdomain(tld_length = @@tld_length)
-
ActionDispatch::Http::URL.extract_subdomain(host, tld_length)
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/hash/keys'
-
1
require 'active_support/core_ext/module/attribute_accessors'
-
-
1
module ActionDispatch
-
1
class Request < Rack::Request
-
1
def cookie_jar
-
env['action_dispatch.cookies'] ||= Cookies::CookieJar.build(self)
-
end
-
end
-
-
# \Cookies are read and written through ActionController#cookies.
-
#
-
# The cookies being read are the ones received along with the request, the cookies
-
# being written will be sent out with the response. Reading a cookie does not get
-
# the cookie object itself back, just the value it holds.
-
#
-
# Examples for writing:
-
#
-
# # Sets a simple session cookie.
-
# # This cookie will be deleted when the user's browser is closed.
-
# cookies[:user_name] = "david"
-
#
-
# # Assign an array of values to a cookie.
-
# cookies[:lat_lon] = [47.68, -122.37]
-
#
-
# # Sets a cookie that expires in 1 hour.
-
# cookies[:login] = { value: "XJ-122", expires: 1.hour.from_now }
-
#
-
# # Sets a signed cookie, which prevents users from tampering with its value.
-
# # The cookie is signed by your app's <tt>config.secret_token</tt> value.
-
# # It can be read using the signed method <tt>cookies.signed[:key]</tt>
-
# cookies.signed[:user_id] = current_user.id
-
#
-
# # Sets a "permanent" cookie (which expires in 20 years from now).
-
# cookies.permanent[:login] = "XJ-122"
-
#
-
# # You can also chain these methods:
-
# cookies.permanent.signed[:login] = "XJ-122"
-
#
-
# Examples for reading:
-
#
-
# cookies[:user_name] # => "david"
-
# cookies.size # => 2
-
# cookies[:lat_lon] # => [47.68, -122.37]
-
# cookies.signed[:login] # => "XJ-122"
-
#
-
# Example for deleting:
-
#
-
# cookies.delete :user_name
-
#
-
# Please note that if you specify a :domain when setting a cookie, you must also specify the domain when deleting the cookie:
-
#
-
# cookies[:key] = {
-
# value: 'a yummy cookie',
-
# expires: 1.year.from_now,
-
# domain: 'domain.com'
-
# }
-
#
-
# cookies.delete(:key, domain: 'domain.com')
-
#
-
# The option symbols for setting cookies are:
-
#
-
# * <tt>:value</tt> - The cookie's value or list of values (as an array).
-
# * <tt>:path</tt> - The path for which this cookie applies. Defaults to the root
-
# of the application.
-
# * <tt>:domain</tt> - The domain for which this cookie applies so you can
-
# restrict to the domain level. If you use a schema like www.example.com
-
# and want to share session with user.example.com set <tt>:domain</tt>
-
# to <tt>:all</tt>. Make sure to specify the <tt>:domain</tt> option with
-
# <tt>:all</tt> again when deleting keys.
-
#
-
# domain: nil # Does not sets cookie domain. (default)
-
# domain: :all # Allow the cookie for the top most level
-
# domain and subdomains.
-
#
-
# * <tt>:expires</tt> - The time at which this cookie expires, as a \Time object.
-
# * <tt>:secure</tt> - Whether this cookie is a only transmitted to HTTPS servers.
-
# Default is +false+.
-
# * <tt>:httponly</tt> - Whether this cookie is accessible via scripting or
-
# only HTTP. Defaults to +false+.
-
1
class Cookies
-
1
HTTP_HEADER = "Set-Cookie".freeze
-
1
TOKEN_KEY = "action_dispatch.secret_token".freeze
-
-
# Raised when storing more than 4K of session data.
-
1
CookieOverflow = Class.new StandardError
-
-
1
class CookieJar #:nodoc:
-
1
include Enumerable
-
-
# This regular expression is used to split the levels of a domain.
-
# The top level domain can be any string without a period or
-
# **.**, ***.** style TLDs like co.uk or com.au
-
#
-
# www.example.co.uk gives:
-
# $& => example.co.uk
-
#
-
# example.com gives:
-
# $& => example.com
-
#
-
# lots.of.subdomains.example.local gives:
-
# $& => example.local
-
1
DOMAIN_REGEXP = /[^.]*\.([^.]*|..\...|...\...)$/
-
-
1
def self.build(request)
-
secret = request.env[TOKEN_KEY]
-
host = request.host
-
secure = request.ssl?
-
-
new(secret, host, secure).tap do |hash|
-
hash.update(request.cookies)
-
end
-
end
-
-
1
def initialize(secret = nil, host = nil, secure = false)
-
@secret = secret
-
@set_cookies = {}
-
@delete_cookies = {}
-
@host = host
-
@secure = secure
-
@cookies = {}
-
end
-
-
1
def each(&block)
-
@cookies.each(&block)
-
end
-
-
# Returns the value of the cookie by +name+, or +nil+ if no such cookie exists.
-
1
def [](name)
-
@cookies[name.to_s]
-
end
-
-
1
def key?(name)
-
@cookies.key?(name.to_s)
-
end
-
1
alias :has_key? :key?
-
-
1
def update(other_hash)
-
@cookies.update other_hash.stringify_keys
-
self
-
end
-
-
1
def handle_options(options) #:nodoc:
-
options[:path] ||= "/"
-
-
if options[:domain] == :all
-
# if there is a provided tld length then we use it otherwise default domain regexp
-
domain_regexp = options[:tld_length] ? /([^.]+\.?){#{options[:tld_length]}}$/ : DOMAIN_REGEXP
-
-
# if host is not ip and matches domain regexp
-
# (ip confirms to domain regexp so we explicitly check for ip)
-
options[:domain] = if (@host !~ /^[\d.]+$/) && (@host =~ domain_regexp)
-
".#{$&}"
-
end
-
elsif options[:domain].is_a? Array
-
# if host matches one of the supplied domains without a dot in front of it
-
options[:domain] = options[:domain].find {|domain| @host.include? domain.sub(/^\./, '') }
-
end
-
end
-
-
# Sets the cookie named +name+. The second argument may be the very cookie
-
# value, or a hash of options as documented above.
-
1
def []=(key, options)
-
if options.is_a?(Hash)
-
options.symbolize_keys!
-
value = options[:value]
-
else
-
value = options
-
options = { :value => value }
-
end
-
-
handle_options(options)
-
-
if @cookies[key.to_s] != value or options[:expires]
-
@cookies[key.to_s] = value
-
@set_cookies[key.to_s] = options
-
@delete_cookies.delete(key.to_s)
-
end
-
-
value
-
end
-
-
# Removes the cookie on the client machine by setting the value to an empty string
-
# and setting its expiration date into the past. Like <tt>[]=</tt>, you can pass in
-
# an options hash to delete cookies with extra data such as a <tt>:path</tt>.
-
1
def delete(key, options = {})
-
return unless @cookies.has_key? key.to_s
-
-
options.symbolize_keys!
-
handle_options(options)
-
-
value = @cookies.delete(key.to_s)
-
@delete_cookies[key.to_s] = options
-
value
-
end
-
-
# Whether the given cookie is to be deleted by this CookieJar.
-
# Like <tt>[]=</tt>, you can pass in an options hash to test if a
-
# deletion applies to a specific <tt>:path</tt>, <tt>:domain</tt> etc.
-
1
def deleted?(key, options = {})
-
options.symbolize_keys!
-
handle_options(options)
-
@delete_cookies[key.to_s] == options
-
end
-
-
# Removes all cookies on the client machine by calling <tt>delete</tt> for each cookie
-
1
def clear(options = {})
-
@cookies.each_key{ |k| delete(k, options) }
-
end
-
-
# Returns a jar that'll automatically set the assigned cookies to have an expiration date 20 years from now. Example:
-
#
-
# cookies.permanent[:prefers_open_id] = true
-
# # => Set-Cookie: prefers_open_id=true; path=/; expires=Sun, 16-Dec-2029 03:24:16 GMT
-
#
-
# This jar is only meant for writing. You'll read permanent cookies through the regular accessor.
-
#
-
# This jar allows chaining with the signed jar as well, so you can set permanent, signed cookies. Examples:
-
#
-
# cookies.permanent.signed[:remember_me] = current_user.id
-
# # => Set-Cookie: remember_me=BAhU--848956038e692d7046deab32b7131856ab20e14e; path=/; expires=Sun, 16-Dec-2029 03:24:16 GMT
-
1
def permanent
-
@permanent ||= PermanentCookieJar.new(self, @secret)
-
end
-
-
# Returns a jar that'll automatically generate a signed representation of cookie value and verify it when reading from
-
# the cookie again. This is useful for creating cookies with values that the user is not supposed to change. If a signed
-
# cookie was tampered with by the user (or a 3rd party), an ActiveSupport::MessageVerifier::InvalidSignature exception will
-
# be raised.
-
#
-
# This jar requires that you set a suitable secret for the verification on your app's +config.secret_token+.
-
#
-
# Example:
-
#
-
# cookies.signed[:discount] = 45
-
# # => Set-Cookie: discount=BAhpMg==--2c1c6906c90a3bc4fd54a51ffb41dffa4bf6b5f7; path=/
-
#
-
# cookies.signed[:discount] # => 45
-
1
def signed
-
@signed ||= SignedCookieJar.new(self, @secret)
-
end
-
-
1
def write(headers)
-
@set_cookies.each { |k, v| ::Rack::Utils.set_cookie_header!(headers, k, v) if write_cookie?(v) }
-
@delete_cookies.each { |k, v| ::Rack::Utils.delete_cookie_header!(headers, k, v) }
-
end
-
-
1
def recycle! #:nodoc:
-
@set_cookies.clear
-
@delete_cookies.clear
-
end
-
-
1
mattr_accessor :always_write_cookie
-
1
self.always_write_cookie = false
-
-
1
private
-
-
1
def write_cookie?(cookie)
-
@secure || !cookie[:secure] || always_write_cookie
-
end
-
end
-
-
1
class PermanentCookieJar < CookieJar #:nodoc:
-
1
def initialize(parent_jar, secret)
-
@parent_jar, @secret = parent_jar, secret
-
end
-
-
1
def []=(key, options)
-
if options.is_a?(Hash)
-
options.symbolize_keys!
-
else
-
options = { :value => options }
-
end
-
-
options[:expires] = 20.years.from_now
-
@parent_jar[key] = options
-
end
-
-
1
def method_missing(method, *arguments, &block)
-
@parent_jar.send(method, *arguments, &block)
-
end
-
end
-
-
1
class SignedCookieJar < CookieJar #:nodoc:
-
1
MAX_COOKIE_SIZE = 4096 # Cookies can typically store 4096 bytes.
-
1
SECRET_MIN_LENGTH = 30 # Characters
-
-
1
def initialize(parent_jar, secret)
-
ensure_secret_secure(secret)
-
@parent_jar = parent_jar
-
@verifier = ActiveSupport::MessageVerifier.new(secret)
-
end
-
-
1
def [](name)
-
if signed_message = @parent_jar[name]
-
@verifier.verify(signed_message)
-
end
-
rescue ActiveSupport::MessageVerifier::InvalidSignature
-
nil
-
end
-
-
1
def []=(key, options)
-
if options.is_a?(Hash)
-
options.symbolize_keys!
-
options[:value] = @verifier.generate(options[:value])
-
else
-
options = { :value => @verifier.generate(options) }
-
end
-
-
raise CookieOverflow if options[:value].size > MAX_COOKIE_SIZE
-
@parent_jar[key] = options
-
end
-
-
1
def method_missing(method, *arguments, &block)
-
@parent_jar.send(method, *arguments, &block)
-
end
-
-
1
protected
-
-
# To prevent users from using something insecure like "Password" we make sure that the
-
# secret they've provided is at least 30 characters in length.
-
1
def ensure_secret_secure(secret)
-
if secret.blank?
-
raise ArgumentError, "A secret is required to generate an " +
-
"integrity hash for cookie session data. Use " +
-
"config.secret_token = \"some secret phrase of at " +
-
"least #{SECRET_MIN_LENGTH} characters\"" +
-
"in config/initializers/secret_token.rb"
-
end
-
-
if secret.length < SECRET_MIN_LENGTH
-
raise ArgumentError, "Secret should be something secure, " +
-
"like \"#{SecureRandom.hex(16)}\". The value you " +
-
"provided, \"#{secret}\", is shorter than the minimum length " +
-
"of #{SECRET_MIN_LENGTH} characters"
-
end
-
end
-
end
-
-
1
def initialize(app)
-
@app = app
-
end
-
-
1
def call(env)
-
status, headers, body = @app.call(env)
-
-
if cookie_jar = env['action_dispatch.cookies']
-
cookie_jar.write(headers)
-
if headers[HTTP_HEADER].respond_to?(:join)
-
headers[HTTP_HEADER] = headers[HTTP_HEADER].join("\n")
-
end
-
end
-
-
[status, headers, body]
-
end
-
end
-
end
-
1
module ActionDispatch
-
1
class Request < Rack::Request
-
# Access the contents of the flash. Use <tt>flash["notice"]</tt> to
-
# read a notice you put there or <tt>flash["notice"] = "hello"</tt>
-
# to put a new one.
-
1
def flash
-
@env[Flash::KEY] ||= (session["flash"] || Flash::FlashHash.new).tap(&:sweep)
-
end
-
end
-
-
# The flash provides a way to pass temporary objects between actions. Anything you place in the flash will be exposed
-
# to the very next action and then cleared out. This is a great way of doing notices and alerts, such as a create
-
# action that sets <tt>flash[:notice] = "Post successfully created"</tt> before redirecting to a display action that can
-
# then expose the flash to its template. Actually, that exposure is automatically done.
-
#
-
# class PostsController < ActionController::Base
-
# def create
-
# # save post
-
# flash[:notice] = "Post successfully created"
-
# redirect_to @post
-
# end
-
#
-
# def show
-
# # doesn't need to assign the flash notice to the template, that's done automatically
-
# end
-
# end
-
#
-
# show.html.erb
-
# <% if flash[:notice] %>
-
# <div class="notice"><%= flash[:notice] %></div>
-
# <% end %>
-
#
-
# Since the +notice+ and +alert+ keys are a common idiom, convenience accessors are available:
-
#
-
# flash.alert = "You must be logged in"
-
# flash.notice = "Post successfully created"
-
#
-
# This example just places a string in the flash, but you can put any object in there. And of course, you can put as
-
# many as you like at a time too. Just remember: They'll be gone by the time the next action has been performed.
-
#
-
# See docs on the FlashHash class for more details about the flash.
-
1
class Flash
-
1
KEY = 'action_dispatch.request.flash_hash'.freeze
-
-
1
class FlashNow #:nodoc:
-
1
attr_accessor :flash
-
-
1
def initialize(flash)
-
@flash = flash
-
end
-
-
1
def []=(k, v)
-
@flash[k] = v
-
@flash.discard(k)
-
v
-
end
-
-
1
def [](k)
-
@flash[k]
-
end
-
-
# Convenience accessor for flash.now[:alert]=
-
1
def alert=(message)
-
self[:alert] = message
-
end
-
-
# Convenience accessor for flash.now[:notice]=
-
1
def notice=(message)
-
self[:notice] = message
-
end
-
end
-
-
# Implementation detail: please do not change the signature of the
-
# FlashHash class. Doing that will likely affect all Rails apps in
-
# production as the FlashHash currently stored in their sessions will
-
# become invalid.
-
1
class FlashHash
-
1
include Enumerable
-
-
1
def initialize #:nodoc:
-
@discard = Set.new
-
@flashes = {}
-
@now = nil
-
end
-
-
1
def initialize_copy(other)
-
if other.now_is_loaded?
-
@now = other.now.dup
-
@now.flash = self
-
end
-
super
-
end
-
-
1
def []=(k, v) #:nodoc:
-
@discard.delete k
-
@flashes[k] = v
-
end
-
-
1
def [](k)
-
@flashes[k]
-
end
-
-
1
def update(h) #:nodoc:
-
@discard.subtract h.keys
-
@flashes.update h
-
self
-
end
-
-
1
def keys
-
@flashes.keys
-
end
-
-
1
def key?(name)
-
@flashes.key? name
-
end
-
-
1
def delete(key)
-
@discard.delete key
-
@flashes.delete key
-
self
-
end
-
-
1
def to_hash
-
@flashes.dup
-
end
-
-
1
def empty?
-
@flashes.empty?
-
end
-
-
1
def clear
-
@discard.clear
-
@flashes.clear
-
end
-
-
1
def each(&block)
-
@flashes.each(&block)
-
end
-
-
1
alias :merge! :update
-
-
1
def replace(h) #:nodoc:
-
@discard.clear
-
@flashes.replace h
-
self
-
end
-
-
# Sets a flash that will not be available to the next action, only to the current.
-
#
-
# flash.now[:message] = "Hello current action"
-
#
-
# This method enables you to use the flash as a central messaging system in your app.
-
# When you need to pass an object to the next action, you use the standard flash assign (<tt>[]=</tt>).
-
# When you need to pass an object to the current action, you use <tt>now</tt>, and your object will
-
# vanish when the current action is done.
-
#
-
# Entries set via <tt>now</tt> are accessed the same way as standard entries: <tt>flash['my-key']</tt>.
-
1
def now
-
@now ||= FlashNow.new(self)
-
end
-
-
# Keeps either the entire current flash or a specific flash entry available for the next action:
-
#
-
# flash.keep # keeps the entire flash
-
# flash.keep(:notice) # keeps only the "notice" entry, the rest of the flash is discarded
-
1
def keep(k = nil)
-
@discard.subtract Array(k || keys)
-
k ? self[k] : self
-
end
-
-
# Marks the entire flash or a single flash entry to be discarded by the end of the current action:
-
#
-
# flash.discard # discard the entire flash at the end of the current action
-
# flash.discard(:warning) # discard only the "warning" entry at the end of the current action
-
1
def discard(k = nil)
-
@discard.merge Array(k || keys)
-
k ? self[k] : self
-
end
-
-
# Mark for removal entries that were kept, and delete unkept ones.
-
#
-
# This method is called automatically by filters, so you generally don't need to care about it.
-
1
def sweep #:nodoc:
-
@discard.each { |k| @flashes.delete k }
-
@discard.replace @flashes.keys
-
end
-
-
# Convenience accessor for flash[:alert]
-
1
def alert
-
self[:alert]
-
end
-
-
# Convenience accessor for flash[:alert]=
-
1
def alert=(message)
-
self[:alert] = message
-
end
-
-
# Convenience accessor for flash[:notice]
-
1
def notice
-
self[:notice]
-
end
-
-
# Convenience accessor for flash[:notice]=
-
1
def notice=(message)
-
self[:notice] = message
-
end
-
-
1
protected
-
1
def now_is_loaded?
-
@now
-
end
-
end
-
-
1
def initialize(app)
-
@app = app
-
end
-
-
1
def call(env)
-
@app.call(env)
-
ensure
-
session = Request::Session.find(env) || {}
-
flash_hash = env[KEY]
-
-
if flash_hash
-
if !flash_hash.empty? || session.key?('flash')
-
session["flash"] = flash_hash
-
new_hash = flash_hash.dup
-
else
-
new_hash = flash_hash
-
end
-
-
env[KEY] = new_hash
-
end
-
-
if (!session.respond_to?(:loaded?) || session.loaded?) && # (reset_session uses {}, which doesn't implement #loaded?)
-
session.key?('flash') && session['flash'].empty?
-
session.delete('flash')
-
end
-
end
-
end
-
end
-
1
require "active_support/inflector/methods"
-
1
require "active_support/dependencies"
-
-
1
module ActionDispatch
-
1
class MiddlewareStack
-
1
class Middleware
-
1
attr_reader :args, :block, :name, :classcache
-
-
1
def initialize(klass_or_name, *args, &block)
-
@klass = nil
-
-
if klass_or_name.respond_to?(:name)
-
@klass = klass_or_name
-
@name = @klass.name
-
else
-
@name = klass_or_name.to_s
-
end
-
-
@classcache = ActiveSupport::Dependencies::Reference
-
@args, @block = args, block
-
end
-
-
1
def klass
-
@klass || classcache[@name]
-
end
-
-
1
def ==(middleware)
-
case middleware
-
when Middleware
-
klass == middleware.klass
-
when Class
-
klass == middleware
-
else
-
normalize(@name) == normalize(middleware)
-
end
-
end
-
-
1
def inspect
-
klass.to_s
-
end
-
-
1
def build(app)
-
klass.new(app, *args, &block)
-
end
-
-
1
private
-
-
1
def normalize(object)
-
object.to_s.strip.sub(/^::/, '')
-
end
-
end
-
-
1
include Enumerable
-
-
1
attr_accessor :middlewares
-
-
1
def initialize(*args)
-
1
@middlewares = []
-
1
yield(self) if block_given?
-
end
-
-
1
def each
-
@middlewares.each { |x| yield x }
-
end
-
-
1
def size
-
middlewares.size
-
end
-
-
1
def last
-
middlewares.last
-
end
-
-
1
def [](i)
-
middlewares[i]
-
end
-
-
1
def unshift(*args, &block)
-
middleware = self.class::Middleware.new(*args, &block)
-
middlewares.unshift(middleware)
-
end
-
-
1
def initialize_copy(other)
-
3
self.middlewares = other.middlewares.dup
-
end
-
-
1
def insert(index, *args, &block)
-
index = assert_index(index, :before)
-
middleware = self.class::Middleware.new(*args, &block)
-
middlewares.insert(index, middleware)
-
end
-
-
1
alias_method :insert_before, :insert
-
-
1
def insert_after(index, *args, &block)
-
index = assert_index(index, :after)
-
insert(index + 1, *args, &block)
-
end
-
-
1
def swap(target, *args, &block)
-
index = assert_index(target, :before)
-
insert(index, *args, &block)
-
middlewares.delete_at(index + 1)
-
end
-
-
1
def delete(target)
-
middlewares.delete target
-
end
-
-
1
def use(*args, &block)
-
middleware = self.class::Middleware.new(*args, &block)
-
middlewares.push(middleware)
-
end
-
-
1
def build(app = nil, &block)
-
app ||= block
-
raise "MiddlewareStack#build requires an app" unless app
-
middlewares.freeze.reverse.inject(app) { |a, e| e.build(a) }
-
end
-
-
1
protected
-
-
1
def assert_index(index, where)
-
i = index.is_a?(Integer) ? index : middlewares.index(index)
-
raise "No such middleware to insert #{where}: #{index.inspect}" unless i
-
i
-
end
-
end
-
end
-
# encoding: UTF-8
-
1
require 'active_support/core_ext/object/to_param'
-
1
require 'active_support/core_ext/regexp'
-
-
1
module ActionDispatch
-
# The routing module provides URL rewriting in native Ruby. It's a way to
-
# redirect incoming requests to controllers and actions. This replaces
-
# mod_rewrite rules. Best of all, Rails' \Routing works with any web server.
-
# Routes are defined in <tt>config/routes.rb</tt>.
-
#
-
# Think of creating routes as drawing a map for your requests. The map tells
-
# them where to go based on some predefined pattern:
-
#
-
# AppName::Application.routes.draw do
-
# Pattern 1 tells some request to go to one place
-
# Pattern 2 tell them to go to another
-
# ...
-
# end
-
#
-
# The following symbols are special:
-
#
-
# :controller maps to your controller name
-
# :action maps to an action with your controllers
-
#
-
# Other names simply map to a parameter as in the case of <tt>:id</tt>.
-
#
-
# == Resources
-
#
-
# Resource routing allows you to quickly declare all of the common routes
-
# for a given resourceful controller. Instead of declaring separate routes
-
# for your +index+, +show+, +new+, +edit+, +create+, +update+ and +destroy+
-
# actions, a resourceful route declares them in a single line of code:
-
#
-
# resources :photos
-
#
-
# Sometimes, you have a resource that clients always look up without
-
# referencing an ID. A common example, /profile always shows the profile of
-
# the currently logged in user. In this case, you can use a singular resource
-
# to map /profile (rather than /profile/:id) to the show action.
-
#
-
# resource :profile
-
#
-
# It's common to have resources that are logically children of other
-
# resources:
-
#
-
# resources :magazines do
-
# resources :ads
-
# end
-
#
-
# You may wish to organize groups of controllers under a namespace. Most
-
# commonly, you might group a number of administrative controllers under
-
# an +admin+ namespace. You would place these controllers under the
-
# <tt>app/controllers/admin</tt> directory, and you can group them together
-
# in your router:
-
#
-
# namespace "admin" do
-
# resources :posts, :comments
-
# end
-
#
-
# Alternately, you can add prefixes to your path without using a separate
-
# directory by using +scope+. +scope+ takes additional options which
-
# apply to all enclosed routes.
-
#
-
# scope path: "/cpanel", as: 'admin' do
-
# resources :posts, :comments
-
# end
-
#
-
# For more, see <tt>Routing::Mapper::Resources#resources</tt>,
-
# <tt>Routing::Mapper::Scoping#namespace</tt>, and
-
# <tt>Routing::Mapper::Scoping#scope</tt>.
-
#
-
# == Named routes
-
#
-
# Routes can be named by passing an <tt>:as</tt> option,
-
# allowing for easy reference within your source as +name_of_route_url+
-
# for the full URL and +name_of_route_path+ for the URI path.
-
#
-
# Example:
-
#
-
# # In routes.rb
-
# match '/login' => 'accounts#login', as: 'login'
-
#
-
# # With render, redirect_to, tests, etc.
-
# redirect_to login_url
-
#
-
# Arguments can be passed as well.
-
#
-
# redirect_to show_item_path(id: 25)
-
#
-
# Use <tt>root</tt> as a shorthand to name a route for the root path "/".
-
#
-
# # In routes.rb
-
# root to: 'blogs#index'
-
#
-
# # would recognize http://www.example.com/ as
-
# params = { controller: 'blogs', action: 'index' }
-
#
-
# # and provide these named routes
-
# root_url # => 'http://www.example.com/'
-
# root_path # => '/'
-
#
-
# Note: when using +controller+, the route is simply named after the
-
# method you call on the block parameter rather than map.
-
#
-
# # In routes.rb
-
# controller :blog do
-
# match 'blog/show' => :list
-
# match 'blog/delete' => :delete
-
# match 'blog/edit/:id' => :edit
-
# end
-
#
-
# # provides named routes for show, delete, and edit
-
# link_to @article.title, show_path(id: @article.id)
-
#
-
# == Pretty URLs
-
#
-
# Routes can generate pretty URLs. For example:
-
#
-
# match '/articles/:year/:month/:day' => 'articles#find_by_id', constraints: {
-
# year: /\d{4}/,
-
# month: /\d{1,2}/,
-
# day: /\d{1,2}/
-
# }
-
#
-
# Using the route above, the URL "http://localhost:3000/articles/2005/11/06"
-
# maps to
-
#
-
# params = {year: '2005', month: '11', day: '06'}
-
#
-
# == Regular Expressions and parameters
-
# You can specify a regular expression to define a format for a parameter.
-
#
-
# controller 'geocode' do
-
# match 'geocode/:postalcode' => :show, constraints: {
-
# postalcode: /\d{5}(-\d{4})?/
-
# }
-
#
-
# Constraints can include the 'ignorecase' and 'extended syntax' regular
-
# expression modifiers:
-
#
-
# controller 'geocode' do
-
# match 'geocode/:postalcode' => :show, constraints: {
-
# postalcode: /hx\d\d\s\d[a-z]{2}/i
-
# }
-
# end
-
#
-
# controller 'geocode' do
-
# match 'geocode/:postalcode' => :show, constraints: {
-
# postalcode: /# Postcode format
-
# \d{5} #Prefix
-
# (-\d{4})? #Suffix
-
# /x
-
# }
-
# end
-
#
-
# Using the multiline match modifier will raise an +ArgumentError+.
-
# Encoding regular expression modifiers are silently ignored. The
-
# match will always use the default encoding or ASCII.
-
#
-
# == Default route
-
#
-
# Consider the following route, which you will find commented out at the
-
# bottom of your generated <tt>config/routes.rb</tt>:
-
#
-
# match ':controller(/:action(/:id))(.:format)'
-
#
-
# This route states that it expects requests to consist of a
-
# <tt>:controller</tt> followed optionally by an <tt>:action</tt> that in
-
# turn is followed optionally by an <tt>:id</tt>, which in turn is followed
-
# optionally by a <tt>:format</tt>.
-
#
-
# Suppose you get an incoming request for <tt>/blog/edit/22</tt>, you'll end
-
# up with:
-
#
-
# params = { controller: 'blog',
-
# action: 'edit',
-
# id: '22'
-
# }
-
#
-
# By not relying on default routes, you improve the security of your
-
# application since not all controller actions, which includes actions you
-
# might add at a later time, are exposed by default.
-
#
-
# == HTTP Methods
-
#
-
# Using the <tt>:via</tt> option when specifying a route allows you to
-
# restrict it to a specific HTTP method. Possible values are <tt>:post</tt>,
-
# <tt>:get</tt>, <tt>:patch</tt>, <tt>:put</tt>, <tt>:delete</tt> and
-
# <tt>:any</tt>. If your route needs to respond to more than one method you
-
# can use an array, e.g. <tt>[ :get, :post ]</tt>. The default value is
-
# <tt>:any</tt> which means that the route will respond to any of the HTTP
-
# methods.
-
#
-
# Examples:
-
#
-
# match 'post/:id' => 'posts#show', via: :get
-
# match 'post/:id' => 'posts#create_comment', via: :post
-
#
-
# Now, if you POST to <tt>/posts/:id</tt>, it will route to the <tt>create_comment</tt> action. A GET on the same
-
# URL will route to the <tt>show</tt> action.
-
#
-
# === HTTP helper methods
-
#
-
# An alternative method of specifying which HTTP method a route should respond to is to use the helper
-
# methods <tt>get</tt>, <tt>post</tt>, <tt>patch</tt>, <tt>put</tt> and <tt>delete</tt>.
-
#
-
# Examples:
-
#
-
# get 'post/:id' => 'posts#show'
-
# post 'post/:id' => 'posts#create_comment'
-
#
-
# This syntax is less verbose and the intention is more apparent to someone else reading your code,
-
# however if your route needs to respond to more than one HTTP method (or all methods) then using the
-
# <tt>:via</tt> option on <tt>match</tt> is preferable.
-
#
-
# == External redirects
-
#
-
# You can redirect any path to another path using the redirect helper in your router:
-
#
-
# match "/stories" => redirect("/posts")
-
#
-
# == Unicode character routes
-
#
-
# You can specify unicode character routes in your router:
-
#
-
# match "こんにちは" => "welcome#index"
-
#
-
# == Routing to Rack Applications
-
#
-
# Instead of a String, like <tt>posts#index</tt>, which corresponds to the
-
# index action in the PostsController, you can specify any Rack application
-
# as the endpoint for a matcher:
-
#
-
# match "/application.js" => Sprockets
-
#
-
# == Reloading routes
-
#
-
# You can reload routes if you feel you must:
-
#
-
# Rails.application.reload_routes!
-
#
-
# This will clear all named routes and reload routes.rb if the file has been modified from
-
# last load. To absolutely force reloading, use <tt>reload!</tt>.
-
#
-
# == Testing Routes
-
#
-
# The two main methods for testing your routes:
-
#
-
# === +assert_routing+
-
#
-
# def test_movie_route_properly_splits
-
# opts = {controller: "plugin", action: "checkout", id: "2"}
-
# assert_routing "plugin/checkout/2", opts
-
# end
-
#
-
# +assert_routing+ lets you test whether or not the route properly resolves into options.
-
#
-
# === +assert_recognizes+
-
#
-
# def test_route_has_options
-
# opts = {controller: "plugin", action: "show", id: "12"}
-
# assert_recognizes opts, "/plugins/show/12"
-
# end
-
#
-
# Note the subtle difference between the two: +assert_routing+ tests that
-
# a URL fits options while +assert_recognizes+ tests that a URL
-
# breaks into parameters properly.
-
#
-
# In tests you can simply pass the URL or named route to +get+ or +post+.
-
#
-
# def send_to_jail
-
# get '/jail'
-
# assert_response :success
-
# assert_template "jail/front"
-
# end
-
#
-
# def goes_to_login
-
# get login_url
-
# #...
-
# end
-
#
-
# == View a list of all your routes
-
#
-
# rake routes
-
#
-
# Target specific controllers by prefixing the command with <tt>CONTROLLER=x</tt>.
-
#
-
1
module Routing
-
1
autoload :Mapper, 'action_dispatch/routing/mapper'
-
1
autoload :RouteSet, 'action_dispatch/routing/route_set'
-
1
autoload :RoutesProxy, 'action_dispatch/routing/routes_proxy'
-
1
autoload :UrlFor, 'action_dispatch/routing/url_for'
-
1
autoload :PolymorphicRoutes, 'action_dispatch/routing/polymorphic_routes'
-
-
1
SEPARATORS = %w( / . ? ) #:nodoc:
-
1
HTTP_METHODS = [:get, :head, :post, :patch, :put, :delete, :options] #:nodoc:
-
end
-
end
-
1
require 'active_support/core_ext/hash/except'
-
1
require 'active_support/core_ext/hash/reverse_merge'
-
1
require 'active_support/core_ext/hash/slice'
-
1
require 'active_support/core_ext/enumerable'
-
1
require 'active_support/inflector'
-
1
require 'action_dispatch/routing/redirection'
-
-
1
module ActionDispatch
-
1
module Routing
-
1
class Mapper
-
1
class Constraints #:nodoc:
-
1
def self.new(app, constraints, request = Rack::Request)
-
3
if constraints.any?
-
super(app, constraints, request)
-
else
-
3
app
-
end
-
end
-
-
1
attr_reader :app, :constraints
-
-
1
def initialize(app, constraints, request)
-
@app, @constraints, @request = app, constraints, request
-
end
-
-
1
def matches?(env)
-
req = @request.new(env)
-
-
@constraints.each { |constraint|
-
if constraint.respond_to?(:matches?) && !constraint.matches?(req)
-
return false
-
elsif constraint.respond_to?(:call) && !constraint.call(*constraint_args(constraint, req))
-
return false
-
end
-
}
-
-
return true
-
ensure
-
req.reset_parameters
-
end
-
-
1
def call(env)
-
matches?(env) ? @app.call(env) : [ 404, {'X-Cascade' => 'pass'}, [] ]
-
end
-
-
1
private
-
1
def constraint_args(constraint, request)
-
constraint.arity == 1 ? [request] : [request.symbolized_path_parameters, request]
-
end
-
end
-
-
1
class Mapping #:nodoc:
-
1
IGNORE_OPTIONS = [:to, :as, :via, :on, :constraints, :defaults, :only, :except, :anchor, :shallow, :shallow_path, :shallow_prefix]
-
1
ANCHOR_CHARACTERS_REGEX = %r{\A(\\A|\^)|(\\Z|\\z|\$)\Z}
-
1
SHORTHAND_REGEX = %r{/[\w/]+$}
-
1
WILDCARD_PATH = %r{\*([^/\)]+)\)?$}
-
-
1
def initialize(set, scope, path, options)
-
3
@set, @scope = set, scope
-
3
@segment_keys = nil
-
3
@options = (@scope[:options] || {}).merge(options)
-
3
@path = normalize_path(path)
-
3
normalize_options!
-
-
3
via_all = @options.delete(:via) if @options[:via] == :all
-
-
3
if !via_all && request_method_condition.empty?
-
msg = "You should not use the `match` method in your router without specifying an HTTP method.\n" \
-
"If you want to expose your action to GET, use `get` in the router:\n\n" \
-
" Instead of: match \"controller#action\"\n" \
-
" Do: get \"controller#action\""
-
raise msg
-
end
-
end
-
-
1
def to_route
-
3
[ app, conditions, requirements, defaults, @options[:as], @options[:anchor] ]
-
end
-
-
1
private
-
-
1
def normalize_options!
-
3
path_without_format = @path.sub(/\(\.:format\)$/, '')
-
-
3
if using_match_shorthand?(path_without_format, @options)
-
to_shorthand = @options[:to].blank?
-
@options[:to] ||= path_without_format.gsub(/\(.*\)/, "")[1..-1].sub(%r{/([^/]*)$}, '#\1')
-
end
-
-
3
@options.merge!(default_controller_and_action(to_shorthand))
-
-
3
requirements.each do |name, requirement|
-
# segment_keys.include?(k.to_s) || k == :controller
-
2
next unless Regexp === requirement && !constraints[name]
-
-
2
if requirement.source =~ ANCHOR_CHARACTERS_REGEX
-
raise ArgumentError, "Regexp anchor characters are not allowed in routing requirements: #{requirement.inspect}"
-
end
-
2
if requirement.multiline?
-
raise ArgumentError, "Regexp multiline option not allowed in routing requirements: #{requirement.inspect}"
-
end
-
end
-
-
3
if @options[:constraints].is_a?(Hash)
-
(@options[:defaults] ||= {}).reverse_merge!(defaults_from_constraints(@options[:constraints]))
-
end
-
end
-
-
# match "account/overview"
-
1
def using_match_shorthand?(path, options)
-
3
path && (options[:to] || options[:action]).nil? && path =~ SHORTHAND_REGEX
-
end
-
-
1
def normalize_path(path)
-
3
raise ArgumentError, "path is required" if path.blank?
-
3
path = Mapper.normalize_path(path)
-
-
3
if path.match(':controller')
-
2
raise ArgumentError, ":controller segment is not allowed within a namespace block" if @scope[:module]
-
-
# Add a default constraint for :controller path segments that matches namespaced
-
# controllers with default routes like :controller/:action/:id(.:format), e.g:
-
# GET /admin/products/show/1
-
# => { controller: 'admin/products', action: 'show', id: '1' }
-
2
@options[:controller] ||= /.+?/
-
end
-
-
# Add a constraint for wildcard route to make it non-greedy and match the
-
# optional format part of the route by default
-
3
if path.match(WILDCARD_PATH) && @options[:format] != false
-
@options[$1.to_sym] ||= /.+?/
-
end
-
-
3
if @options[:format] == false
-
@options.delete(:format)
-
path
-
3
elsif path.include?(":format") || path.end_with?('/')
-
path
-
3
elsif @options[:format] == true
-
"#{path}.:format"
-
else
-
3
"#{path}(.:format)"
-
end
-
end
-
-
1
def app
-
3
Constraints.new(
-
to.respond_to?(:call) ? to : Routing::RouteSet::Dispatcher.new(:defaults => defaults),
-
blocks,
-
@set.request_class
-
)
-
end
-
-
1
def conditions
-
3
{ :path_info => @path }.merge(constraints).merge(request_method_condition)
-
end
-
-
1
def requirements
-
3
@requirements ||= (@options[:constraints].is_a?(Hash) ? @options[:constraints] : {}).tap do |requirements|
-
3
requirements.reverse_merge!(@scope[:constraints]) if @scope[:constraints]
-
17
@options.each { |k, v| requirements[k] = v if v.is_a?(Regexp) }
-
11
end
-
end
-
-
1
def defaults
-
3
@defaults ||= (@options[:defaults] || {}).tap do |defaults|
-
3
defaults.reverse_merge!(@scope[:defaults]) if @scope[:defaults]
-
17
@options.each { |k, v| defaults[k] = v unless v.is_a?(Regexp) || IGNORE_OPTIONS.include?(k.to_sym) }
-
6
end
-
end
-
-
1
def default_controller_and_action(to_shorthand=nil)
-
3
if to.respond_to?(:call)
-
{ }
-
else
-
3
if to.is_a?(String)
-
1
controller, action = to.split('#')
-
elsif to.is_a?(Symbol)
-
action = to.to_s
-
end
-
-
3
controller ||= default_controller
-
3
action ||= default_action
-
-
3
unless controller.is_a?(Regexp) || to_shorthand
-
1
controller = [@scope[:module], controller].compact.join("/").presence
-
end
-
-
3
if controller.is_a?(String) && controller =~ %r{\A/}
-
raise ArgumentError, "controller name should not start with a slash"
-
end
-
-
3
controller = controller.to_s unless controller.is_a?(Regexp)
-
3
action = action.to_s unless action.is_a?(Regexp)
-
-
3
if controller.blank? && segment_keys.exclude?("controller")
-
raise ArgumentError, "missing :controller"
-
end
-
-
3
if action.blank? && segment_keys.exclude?("action")
-
raise ArgumentError, "missing :action"
-
end
-
-
3
hash = {}
-
3
hash[:controller] = controller unless controller.blank?
-
3
hash[:action] = action unless action.blank?
-
3
hash
-
end
-
end
-
-
1
def blocks
-
3
constraints = @options[:constraints]
-
3
if constraints.present? && !constraints.is_a?(Hash)
-
[constraints]
-
else
-
3
@scope[:blocks] || []
-
end
-
end
-
-
1
def constraints
-
7
@constraints ||= requirements.reject { |k, v| segment_keys.include?(k.to_s) || k == :controller }
-
end
-
-
1
def request_method_condition
-
6
if via = @options[:via]
-
12
list = Array(via).map { |m| m.to_s.dasherize.upcase }
-
6
{ :request_method => list }
-
else
-
{ }
-
end
-
end
-
-
1
def segment_keys
-
4
return @segment_keys if @segment_keys
-
-
2
@segment_keys = Journey::Path::Pattern.new(
-
Journey::Router::Strexp.compile(@path, requirements, SEPARATORS)
-
).names
-
end
-
-
1
def to
-
12
@options[:to]
-
end
-
-
1
def default_controller
-
2
@options[:controller] || @scope[:controller]
-
end
-
-
1
def default_action
-
2
@options[:action] || @scope[:action]
-
end
-
-
1
def defaults_from_constraints(constraints)
-
url_keys = [:protocol, :subdomain, :domain, :host, :port]
-
constraints.slice(*url_keys).select{ |k, v| v.is_a?(String) || v.is_a?(Fixnum) }
-
end
-
end
-
-
# Invokes Rack::Mount::Utils.normalize path and ensure that
-
# (:locale) becomes (/:locale) instead of /(:locale). Except
-
# for root cases, where the latter is the correct one.
-
1
def self.normalize_path(path)
-
6
path = Journey::Router::Utils.normalize_path(path)
-
6
path.gsub!(%r{/(\(+)/?}, '\1/') unless path =~ %r{^/\(+[^)]+\)$}
-
6
path
-
end
-
-
1
def self.normalize_name(name)
-
3
normalize_path(name)[1..-1].tr("/", "_")
-
end
-
-
1
module Base
-
# You can specify what Rails should route "/" to with the root method:
-
#
-
# root to: 'pages#main'
-
#
-
# For options, see +match+, as +root+ uses it internally.
-
#
-
# You can also pass a string which will expand
-
#
-
# root 'pages#main'
-
#
-
# You should put the root route at the top of <tt>config/routes.rb</tt>,
-
# because this means it will be matched first. As this is the most popular route
-
# of most Rails applications, this is beneficial.
-
1
def root(options = {})
-
options = { :to => options } if options.is_a?(String)
-
match '/', { :as => :root, :via => :get }.merge(options)
-
end
-
-
# Matches a url pattern to one or more routes. Any symbols in a pattern
-
# are interpreted as url query parameters and thus available as +params+
-
# in an action:
-
#
-
# # sets :controller, :action and :id in params
-
# match ':controller/:action/:id'
-
#
-
# Two of these symbols are special, +:controller+ maps to the controller
-
# and +:action+ to the controller's action. A pattern can also map
-
# wildcard segments (globs) to params:
-
#
-
# match 'songs/*category/:title' => 'songs#show'
-
#
-
# # 'songs/rock/classic/stairway-to-heaven' sets
-
# # params[:category] = 'rock/classic'
-
# # params[:title] = 'stairway-to-heaven'
-
#
-
# When a pattern points to an internal route, the route's +:action+ and
-
# +:controller+ should be set in options or hash shorthand. Examples:
-
#
-
# match 'photos/:id' => 'photos#show'
-
# match 'photos/:id', to: 'photos#show'
-
# match 'photos/:id', controller: 'photos', action: 'show'
-
#
-
# A pattern can also point to a +Rack+ endpoint i.e. anything that
-
# responds to +call+:
-
#
-
# match 'photos/:id' => lambda {|hash| [200, {}, "Coming soon"] }
-
# match 'photos/:id' => PhotoRackApp
-
# # Yes, controller actions are just rack endpoints
-
# match 'photos/:id' => PhotosController.action(:show)
-
#
-
# === Options
-
#
-
# Any options not seen here are passed on as params with the url.
-
#
-
# [:controller]
-
# The route's controller.
-
#
-
# [:action]
-
# The route's action.
-
#
-
# [:path]
-
# The path prefix for the routes.
-
#
-
# [:module]
-
# The namespace for :controller.
-
#
-
# match 'path' => 'c#a', module: 'sekret', controller: 'posts'
-
# #=> Sekret::PostsController
-
#
-
# See <tt>Scoping#namespace</tt> for its scope equivalent.
-
#
-
# [:as]
-
# The name used to generate routing helpers.
-
#
-
# [:via]
-
# Allowed HTTP verb(s) for route.
-
#
-
# match 'path' => 'c#a', via: :get
-
# match 'path' => 'c#a', via: [:get, :post]
-
#
-
# [:to]
-
# Points to a +Rack+ endpoint. Can be an object that responds to
-
# +call+ or a string representing a controller's action.
-
#
-
# match 'path', to: 'controller#action'
-
# match 'path', to: lambda { |env| [200, {}, "Success!"] }
-
# match 'path', to: RackApp
-
#
-
# [:on]
-
# Shorthand for wrapping routes in a specific RESTful context. Valid
-
# values are +:member+, +:collection+, and +:new+. Only use within
-
# <tt>resource(s)</tt> block. For example:
-
#
-
# resource :bar do
-
# match 'foo' => 'c#a', on: :member, via: [:get, :post]
-
# end
-
#
-
# Is equivalent to:
-
#
-
# resource :bar do
-
# member do
-
# match 'foo' => 'c#a', via: [:get, :post]
-
# end
-
# end
-
#
-
# [:constraints]
-
# Constrains parameters with a hash of regular expressions or an
-
# object that responds to <tt>matches?</tt>
-
#
-
# match 'path/:id', constraints: { id: /[A-Z]\d{5}/ }
-
#
-
# class Blacklist
-
# def matches?(request) request.remote_ip == '1.2.3.4' end
-
# end
-
# match 'path' => 'c#a', constraints: Blacklist.new
-
#
-
# See <tt>Scoping#constraints</tt> for more examples with its scope
-
# equivalent.
-
#
-
# [:defaults]
-
# Sets defaults for parameters
-
#
-
# # Sets params[:format] to 'jpg' by default
-
# match 'path' => 'c#a', defaults: { format: 'jpg' }
-
#
-
# See <tt>Scoping#defaults</tt> for its scope equivalent.
-
#
-
# [:anchor]
-
# Boolean to anchor a <tt>match</tt> pattern. Default is true. When set to
-
# false, the pattern matches any request prefixed with the given path.
-
#
-
# # Matches any request starting with 'path'
-
# match 'path' => 'c#a', anchor: false
-
#
-
# [:format]
-
# Allows you to specify the default value for optional +format+
-
# segment or disable it by supplying +false+.
-
1
def match(path, options=nil)
-
end
-
-
# Mount a Rack-based application to be used within the application.
-
#
-
# mount SomeRackApp, at: "some_route"
-
#
-
# Alternatively:
-
#
-
# mount(SomeRackApp => "some_route")
-
#
-
# For options, see +match+, as +mount+ uses it internally.
-
#
-
# All mounted applications come with routing helpers to access them.
-
# These are named after the class specified, so for the above example
-
# the helper is either +some_rack_app_path+ or +some_rack_app_url+.
-
# To customize this helper's name, use the +:as+ option:
-
#
-
# mount(SomeRackApp => "some_route", as: "exciting")
-
#
-
# This will generate the +exciting_path+ and +exciting_url+ helpers
-
# which can be used to navigate to this mounted app.
-
1
def mount(app, options = nil)
-
if options
-
path = options.delete(:at)
-
else
-
unless Hash === app
-
raise ArgumentError, "must be called with mount point"
-
end
-
-
options = app
-
app, path = options.find { |k, v| k.respond_to?(:call) }
-
options.delete(app) if app
-
end
-
-
raise "A rack application must be specified" unless path
-
-
options[:as] ||= app_name(app)
-
options[:via] ||= :all
-
-
match(path, options.merge(:to => app, :anchor => false, :format => false))
-
-
define_generate_prefix(app, options[:as])
-
self
-
end
-
-
1
def default_url_options=(options)
-
@set.default_url_options = options
-
end
-
1
alias_method :default_url_options, :default_url_options=
-
-
1
def with_default_scope(scope, &block)
-
scope(scope) do
-
instance_exec(&block)
-
end
-
end
-
-
1
private
-
1
def app_name(app)
-
return unless app.respond_to?(:routes)
-
-
if app.respond_to?(:railtie_name)
-
app.railtie_name
-
else
-
class_name = app.class.is_a?(Class) ? app.name : app.class.name
-
ActiveSupport::Inflector.underscore(class_name).tr("/", "_")
-
end
-
end
-
-
1
def define_generate_prefix(app, name)
-
return unless app.respond_to?(:routes) && app.routes.respond_to?(:define_mounted_helper)
-
-
_route = @set.named_routes.routes[name.to_sym]
-
_routes = @set
-
app.routes.define_mounted_helper(name)
-
app.routes.singleton_class.class_eval do
-
define_method :mounted? do
-
true
-
end
-
-
define_method :_generate_prefix do |options|
-
prefix_options = options.slice(*_route.segment_keys)
-
# we must actually delete prefix segment keys to avoid passing them to next url_for
-
_route.segment_keys.each { |k| options.delete(k) }
-
_routes.url_helpers.send("#{name}_path", prefix_options)
-
end
-
end
-
end
-
end
-
-
1
module HttpHelpers
-
# Define a route that only recognizes HTTP GET.
-
# For supported arguments, see <tt>Base#match</tt>.
-
#
-
# get 'bacon', to: 'food#bacon'
-
1
def get(*args, &block)
-
3
map_method(:get, args, &block)
-
end
-
-
# Define a route that only recognizes HTTP POST.
-
# For supported arguments, see <tt>Base#match</tt>.
-
#
-
# post 'bacon', to: 'food#bacon'
-
1
def post(*args, &block)
-
map_method(:post, args, &block)
-
end
-
-
# Define a route that only recognizes HTTP PATCH.
-
# For supported arguments, see <tt>Base#match</tt>.
-
#
-
# patch 'bacon', to: 'food#bacon'
-
1
def patch(*args, &block)
-
map_method(:patch, args, &block)
-
end
-
-
# Define a route that only recognizes HTTP PUT.
-
# For supported arguments, see <tt>Base#match</tt>.
-
#
-
# put 'bacon', to: 'food#bacon'
-
1
def put(*args, &block)
-
map_method(:put, args, &block)
-
end
-
-
# Define a route that only recognizes HTTP DELETE.
-
# For supported arguments, see <tt>Base#match</tt>.
-
#
-
# delete 'broccoli', to: 'food#broccoli'
-
1
def delete(*args, &block)
-
map_method(:delete, args, &block)
-
end
-
-
1
private
-
1
def map_method(method, args, &block)
-
3
options = args.extract_options!
-
3
options[:via] = method
-
3
options[:path] ||= args.first if args.first.is_a?(String)
-
3
match(*args, options, &block)
-
3
self
-
end
-
end
-
-
# You may wish to organize groups of controllers under a namespace.
-
# Most commonly, you might group a number of administrative controllers
-
# under an +admin+ namespace. You would place these controllers under
-
# the <tt>app/controllers/admin</tt> directory, and you can group them
-
# together in your router:
-
#
-
# namespace "admin" do
-
# resources :posts, :comments
-
# end
-
#
-
# This will create a number of routes for each of the posts and comments
-
# controller. For <tt>Admin::PostsController</tt>, Rails will create:
-
#
-
# GET /admin/posts
-
# GET /admin/posts/new
-
# POST /admin/posts
-
# GET /admin/posts/1
-
# GET /admin/posts/1/edit
-
# PATCH/PUT /admin/posts/1
-
# DELETE /admin/posts/1
-
#
-
# If you want to route /posts (without the prefix /admin) to
-
# <tt>Admin::PostsController</tt>, you could use
-
#
-
# scope module: "admin" do
-
# resources :posts
-
# end
-
#
-
# or, for a single case
-
#
-
# resources :posts, module: "admin"
-
#
-
# If you want to route /admin/posts to +PostsController+
-
# (without the Admin:: module prefix), you could use
-
#
-
# scope "/admin" do
-
# resources :posts
-
# end
-
#
-
# or, for a single case
-
#
-
# resources :posts, path: "/admin/posts"
-
#
-
# In each of these cases, the named routes remain the same as if you did
-
# not use scope. In the last case, the following paths map to
-
# +PostsController+:
-
#
-
# GET /admin/posts
-
# GET /admin/posts/new
-
# POST /admin/posts
-
# GET /admin/posts/1
-
# GET /admin/posts/1/edit
-
# PATCH/PUT /admin/posts/1
-
# DELETE /admin/posts/1
-
1
module Scoping
-
# Scopes a set of routes to the given default options.
-
#
-
# Take the following route definition as an example:
-
#
-
# scope path: ":account_id", as: "account" do
-
# resources :projects
-
# end
-
#
-
# This generates helpers such as +account_projects_path+, just like +resources+ does.
-
# The difference here being that the routes generated are like /:account_id/projects,
-
# rather than /accounts/:account_id/projects.
-
#
-
# === Options
-
#
-
# Takes same options as <tt>Base#match</tt> and <tt>Resources#resources</tt>.
-
#
-
# === Examples
-
#
-
# # route /posts (without the prefix /admin) to <tt>Admin::PostsController</tt>
-
# scope module: "admin" do
-
# resources :posts
-
# end
-
#
-
# # prefix the posts resource's requests with '/admin'
-
# scope path: "/admin" do
-
# resources :posts
-
# end
-
#
-
# # prefix the routing helper name: +sekret_posts_path+ instead of +posts_path+
-
# scope as: "sekret" do
-
# resources :posts
-
# end
-
1
def scope(*args)
-
options = args.extract_options!
-
options = options.dup
-
-
options[:path] = args.first if args.first.is_a?(String)
-
recover = {}
-
-
options[:constraints] ||= {}
-
unless options[:constraints].is_a?(Hash)
-
block, options[:constraints] = options[:constraints], {}
-
end
-
-
if options[:constraints].is_a?(Hash)
-
(options[:defaults] ||= {}).reverse_merge!(defaults_from_constraints(options[:constraints]))
-
end
-
-
scope_options.each do |option|
-
if value = options.delete(option)
-
recover[option] = @scope[option]
-
@scope[option] = send("merge_#{option}_scope", @scope[option], value)
-
end
-
end
-
-
recover[:block] = @scope[:blocks]
-
@scope[:blocks] = merge_blocks_scope(@scope[:blocks], block)
-
-
recover[:options] = @scope[:options]
-
@scope[:options] = merge_options_scope(@scope[:options], options)
-
-
yield
-
self
-
ensure
-
scope_options.each do |option|
-
@scope[option] = recover[option] if recover.has_key?(option)
-
end
-
-
@scope[:options] = recover[:options]
-
@scope[:blocks] = recover[:block]
-
end
-
-
# Scopes routes to a specific controller
-
#
-
# controller "food" do
-
# match "bacon", action: "bacon"
-
# end
-
1
def controller(controller, options={})
-
options[:controller] = controller
-
scope(options) { yield }
-
end
-
-
# Scopes routes to a specific namespace. For example:
-
#
-
# namespace :admin do
-
# resources :posts
-
# end
-
#
-
# This generates the following routes:
-
#
-
# admin_posts GET /admin/posts(.:format) admin/posts#index
-
# admin_posts POST /admin/posts(.:format) admin/posts#create
-
# new_admin_post GET /admin/posts/new(.:format) admin/posts#new
-
# edit_admin_post GET /admin/posts/:id/edit(.:format) admin/posts#edit
-
# admin_post GET /admin/posts/:id(.:format) admin/posts#show
-
# admin_post PATCH/PUT /admin/posts/:id(.:format) admin/posts#update
-
# admin_post DELETE /admin/posts/:id(.:format) admin/posts#destroy
-
#
-
# === Options
-
#
-
# The +:path+, +:as+, +:module+, +:shallow_path+ and +:shallow_prefix+
-
# options all default to the name of the namespace.
-
#
-
# For options, see <tt>Base#match</tt>. For +:shallow_path+ option, see
-
# <tt>Resources#resources</tt>.
-
#
-
# === Examples
-
#
-
# # accessible through /sekret/posts rather than /admin/posts
-
# namespace :admin, path: "sekret" do
-
# resources :posts
-
# end
-
#
-
# # maps to <tt>Sekret::PostsController</tt> rather than <tt>Admin::PostsController</tt>
-
# namespace :admin, module: "sekret" do
-
# resources :posts
-
# end
-
#
-
# # generates +sekret_posts_path+ rather than +admin_posts_path+
-
# namespace :admin, as: "sekret" do
-
# resources :posts
-
# end
-
1
def namespace(path, options = {})
-
path = path.to_s
-
options = { :path => path, :as => path, :module => path,
-
:shallow_path => path, :shallow_prefix => path }.merge!(options)
-
scope(options) { yield }
-
end
-
-
# === Parameter Restriction
-
# Allows you to constrain the nested routes based on a set of rules.
-
# For instance, in order to change the routes to allow for a dot character in the +id+ parameter:
-
#
-
# constraints(id: /\d+\.\d+/) do
-
# resources :posts
-
# end
-
#
-
# Now routes such as +/posts/1+ will no longer be valid, but +/posts/1.1+ will be.
-
# The +id+ parameter must match the constraint passed in for this example.
-
#
-
# You may use this to also restrict other parameters:
-
#
-
# resources :posts do
-
# constraints(post_id: /\d+\.\d+/) do
-
# resources :comments
-
# end
-
# end
-
#
-
# === Restricting based on IP
-
#
-
# Routes can also be constrained to an IP or a certain range of IP addresses:
-
#
-
# constraints(ip: /192\.168\.\d+\.\d+/) do
-
# resources :posts
-
# end
-
#
-
# Any user connecting from the 192.168.* range will be able to see this resource,
-
# where as any user connecting outside of this range will be told there is no such route.
-
#
-
# === Dynamic request matching
-
#
-
# Requests to routes can be constrained based on specific criteria:
-
#
-
# constraints(lambda { |req| req.env["HTTP_USER_AGENT"] =~ /iPhone/ }) do
-
# resources :iphones
-
# end
-
#
-
# You are able to move this logic out into a class if it is too complex for routes.
-
# This class must have a +matches?+ method defined on it which either returns +true+
-
# if the user should be given access to that route, or +false+ if the user should not.
-
#
-
# class Iphone
-
# def self.matches?(request)
-
# request.env["HTTP_USER_AGENT"] =~ /iPhone/
-
# end
-
# end
-
#
-
# An expected place for this code would be +lib/constraints+.
-
#
-
# This class is then used like this:
-
#
-
# constraints(Iphone) do
-
# resources :iphones
-
# end
-
1
def constraints(constraints = {})
-
scope(:constraints => constraints) { yield }
-
end
-
-
# Allows you to set default parameters for a route, such as this:
-
# defaults id: 'home' do
-
# match 'scoped_pages/(:id)', to: 'pages#show'
-
# end
-
# Using this, the +:id+ parameter here will default to 'home'.
-
1
def defaults(defaults = {})
-
scope(:defaults => defaults) { yield }
-
end
-
-
1
private
-
1
def scope_options #:nodoc:
-
@scope_options ||= private_methods.grep(/^merge_(.+)_scope$/) { $1.to_sym }
-
end
-
-
1
def merge_path_scope(parent, child) #:nodoc:
-
Mapper.normalize_path("#{parent}/#{child}")
-
end
-
-
1
def merge_shallow_path_scope(parent, child) #:nodoc:
-
Mapper.normalize_path("#{parent}/#{child}")
-
end
-
-
1
def merge_as_scope(parent, child) #:nodoc:
-
parent ? "#{parent}_#{child}" : child
-
end
-
-
1
def merge_shallow_prefix_scope(parent, child) #:nodoc:
-
parent ? "#{parent}_#{child}" : child
-
end
-
-
1
def merge_module_scope(parent, child) #:nodoc:
-
parent ? "#{parent}/#{child}" : child
-
end
-
-
1
def merge_controller_scope(parent, child) #:nodoc:
-
child
-
end
-
-
1
def merge_path_names_scope(parent, child) #:nodoc:
-
merge_options_scope(parent, child)
-
end
-
-
1
def merge_constraints_scope(parent, child) #:nodoc:
-
merge_options_scope(parent, child)
-
end
-
-
1
def merge_defaults_scope(parent, child) #:nodoc:
-
merge_options_scope(parent, child)
-
end
-
-
1
def merge_blocks_scope(parent, child) #:nodoc:
-
merged = parent ? parent.dup : []
-
merged << child if child
-
merged
-
end
-
-
1
def merge_options_scope(parent, child) #:nodoc:
-
(parent || {}).except(*override_keys(child)).merge(child)
-
end
-
-
1
def merge_shallow_scope(parent, child) #:nodoc:
-
child ? true : false
-
end
-
-
1
def override_keys(child) #:nodoc:
-
child.key?(:only) || child.key?(:except) ? [:only, :except] : []
-
end
-
-
1
def defaults_from_constraints(constraints)
-
url_keys = [:protocol, :subdomain, :domain, :host, :port]
-
constraints.slice(*url_keys).select{ |k, v| v.is_a?(String) || v.is_a?(Fixnum) }
-
end
-
end
-
-
# Resource routing allows you to quickly declare all of the common routes
-
# for a given resourceful controller. Instead of declaring separate routes
-
# for your +index+, +show+, +new+, +edit+, +create+, +update+ and +destroy+
-
# actions, a resourceful route declares them in a single line of code:
-
#
-
# resources :photos
-
#
-
# Sometimes, you have a resource that clients always look up without
-
# referencing an ID. A common example, /profile always shows the profile of
-
# the currently logged in user. In this case, you can use a singular resource
-
# to map /profile (rather than /profile/:id) to the show action.
-
#
-
# resource :profile
-
#
-
# It's common to have resources that are logically children of other
-
# resources:
-
#
-
# resources :magazines do
-
# resources :ads
-
# end
-
#
-
# You may wish to organize groups of controllers under a namespace. Most
-
# commonly, you might group a number of administrative controllers under
-
# an +admin+ namespace. You would place these controllers under the
-
# <tt>app/controllers/admin</tt> directory, and you can group them together
-
# in your router:
-
#
-
# namespace "admin" do
-
# resources :posts, :comments
-
# end
-
#
-
# By default the +:id+ parameter doesn't accept dots. If you need to
-
# use dots as part of the +:id+ parameter add a constraint which
-
# overrides this restriction, e.g:
-
#
-
# resources :articles, id: /[^\/]+/
-
#
-
# This allows any character other than a slash as part of your +:id+.
-
#
-
1
module Resources
-
# CANONICAL_ACTIONS holds all actions that does not need a prefix or
-
# a path appended since they fit properly in their scope level.
-
1
VALID_ON_OPTIONS = [:new, :collection, :member]
-
1
RESOURCE_OPTIONS = [:as, :controller, :path, :only, :except, :param, :concerns]
-
1
CANONICAL_ACTIONS = %w(index create new show update destroy)
-
-
1
class Resource #:nodoc:
-
1
attr_reader :controller, :path, :options, :param
-
-
1
def initialize(entities, options = {})
-
@name = entities.to_s
-
@path = (options[:path] || @name).to_s
-
@controller = (options[:controller] || @name).to_s
-
@as = options[:as]
-
@param = (options[:param] || :id).to_sym
-
@options = options
-
end
-
-
1
def default_actions
-
[:index, :create, :new, :show, :update, :destroy, :edit]
-
end
-
-
1
def actions
-
if only = @options[:only]
-
Array(only).map(&:to_sym)
-
elsif except = @options[:except]
-
default_actions - Array(except).map(&:to_sym)
-
else
-
default_actions
-
end
-
end
-
-
1
def name
-
@as || @name
-
end
-
-
1
def plural
-
@plural ||= name.to_s
-
end
-
-
1
def singular
-
@singular ||= name.to_s.singularize
-
end
-
-
1
alias :member_name :singular
-
-
# Checks for uncountable plurals, and appends "_index" if the plural
-
# and singular form are the same.
-
1
def collection_name
-
singular == plural ? "#{plural}_index" : plural
-
end
-
-
1
def resource_scope
-
{ :controller => controller }
-
end
-
-
1
alias :collection_scope :path
-
-
1
def member_scope
-
"#{path}/:#{param}"
-
end
-
-
1
alias :shallow_scope :member_scope
-
-
1
def new_scope(new_path)
-
"#{path}/#{new_path}"
-
end
-
-
1
def nested_param
-
:"#{singular}_#{param}"
-
end
-
-
1
def nested_scope
-
"#{path}/:#{nested_param}"
-
end
-
-
end
-
-
1
class SingletonResource < Resource #:nodoc:
-
1
def initialize(entities, options)
-
super
-
@as = nil
-
@controller = (options[:controller] || plural).to_s
-
@as = options[:as]
-
end
-
-
1
def default_actions
-
[:show, :create, :update, :destroy, :new, :edit]
-
end
-
-
1
def plural
-
@plural ||= name.to_s.pluralize
-
end
-
-
1
def singular
-
@singular ||= name.to_s
-
end
-
-
1
alias :member_name :singular
-
1
alias :collection_name :singular
-
-
1
alias :member_scope :path
-
1
alias :nested_scope :path
-
end
-
-
1
def resources_path_names(options)
-
@scope[:path_names].merge!(options)
-
end
-
-
# Sometimes, you have a resource that clients always look up without
-
# referencing an ID. A common example, /profile always shows the
-
# profile of the currently logged in user. In this case, you can use
-
# a singular resource to map /profile (rather than /profile/:id) to
-
# the show action:
-
#
-
# resource :geocoder
-
#
-
# creates six different routes in your application, all mapping to
-
# the +GeoCoders+ controller (note that the controller is named after
-
# the plural):
-
#
-
# GET /geocoder/new
-
# POST /geocoder
-
# GET /geocoder
-
# GET /geocoder/edit
-
# PATCH/PUT /geocoder
-
# DELETE /geocoder
-
#
-
# === Options
-
# Takes same options as +resources+.
-
1
def resource(*resources, &block)
-
options = resources.extract_options!.dup
-
-
if apply_common_behavior_for(:resource, resources, options, &block)
-
return self
-
end
-
-
resource_scope(:resource, SingletonResource.new(resources.pop, options)) do
-
yield if block_given?
-
-
concerns(options[:concerns]) if options[:concerns]
-
-
collection do
-
post :create
-
end if parent_resource.actions.include?(:create)
-
-
new do
-
get :new
-
end if parent_resource.actions.include?(:new)
-
-
member do
-
get :edit if parent_resource.actions.include?(:edit)
-
get :show if parent_resource.actions.include?(:show)
-
if parent_resource.actions.include?(:update)
-
patch :update
-
put :update
-
end
-
delete :destroy if parent_resource.actions.include?(:destroy)
-
end
-
end
-
-
self
-
end
-
-
# In Rails, a resourceful route provides a mapping between HTTP verbs
-
# and URLs and controller actions. By convention, each action also maps
-
# to particular CRUD operations in a database. A single entry in the
-
# routing file, such as
-
#
-
# resources :photos
-
#
-
# creates seven different routes in your application, all mapping to
-
# the +Photos+ controller:
-
#
-
# GET /photos
-
# GET /photos/new
-
# POST /photos
-
# GET /photos/:id
-
# GET /photos/:id/edit
-
# PATCH/PUT /photos/:id
-
# DELETE /photos/:id
-
#
-
# Resources can also be nested infinitely by using this block syntax:
-
#
-
# resources :photos do
-
# resources :comments
-
# end
-
#
-
# This generates the following comments routes:
-
#
-
# GET /photos/:photo_id/comments
-
# GET /photos/:photo_id/comments/new
-
# POST /photos/:photo_id/comments
-
# GET /photos/:photo_id/comments/:id
-
# GET /photos/:photo_id/comments/:id/edit
-
# PATCH/PUT /photos/:photo_id/comments/:id
-
# DELETE /photos/:photo_id/comments/:id
-
#
-
# === Options
-
# Takes same options as <tt>Base#match</tt> as well as:
-
#
-
# [:path_names]
-
# Allows you to change the segment component of the +edit+ and +new+ actions.
-
# Actions not specified are not changed.
-
#
-
# resources :posts, path_names: { new: "brand_new" }
-
#
-
# The above example will now change /posts/new to /posts/brand_new
-
#
-
# [:path]
-
# Allows you to change the path prefix for the resource.
-
#
-
# resources :posts, path: 'postings'
-
#
-
# The resource and all segments will now route to /postings instead of /posts
-
#
-
# [:only]
-
# Only generate routes for the given actions.
-
#
-
# resources :cows, only: :show
-
# resources :cows, only: [:show, :index]
-
#
-
# [:except]
-
# Generate all routes except for the given actions.
-
#
-
# resources :cows, except: :show
-
# resources :cows, except: [:show, :index]
-
#
-
# [:shallow]
-
# Generates shallow routes for nested resource(s). When placed on a parent resource,
-
# generates shallow routes for all nested resources.
-
#
-
# resources :posts, shallow: true do
-
# resources :comments
-
# end
-
#
-
# Is the same as:
-
#
-
# resources :posts do
-
# resources :comments, except: [:show, :edit, :update, :destroy]
-
# end
-
# resources :comments, only: [:show, :edit, :update, :destroy]
-
#
-
# This allows URLs for resources that otherwise would be deeply nested such
-
# as a comment on a blog post like <tt>/posts/a-long-permalink/comments/1234</tt>
-
# to be shortened to just <tt>/comments/1234</tt>.
-
#
-
# [:shallow_path]
-
# Prefixes nested shallow routes with the specified path.
-
#
-
# scope shallow_path: "sekret" do
-
# resources :posts do
-
# resources :comments, shallow: true
-
# end
-
# end
-
#
-
# The +comments+ resource here will have the following routes generated for it:
-
#
-
# post_comments GET /posts/:post_id/comments(.:format)
-
# post_comments POST /posts/:post_id/comments(.:format)
-
# new_post_comment GET /posts/:post_id/comments/new(.:format)
-
# edit_comment GET /sekret/comments/:id/edit(.:format)
-
# comment GET /sekret/comments/:id(.:format)
-
# comment PATCH/PUT /sekret/comments/:id(.:format)
-
# comment DELETE /sekret/comments/:id(.:format)
-
#
-
# [:shallow_prefix]
-
# Prefixes nested shallow route names with specified prefix.
-
#
-
# scope shallow_prefix: "sekret" do
-
# resources :posts do
-
# resources :comments, shallow: true
-
# end
-
# end
-
#
-
# The +comments+ resource here will have the following routes generated for it:
-
#
-
# post_comments GET /posts/:post_id/comments(.:format)
-
# post_comments POST /posts/:post_id/comments(.:format)
-
# new_post_comment GET /posts/:post_id/comments/new(.:format)
-
# edit_sekret_comment GET /comments/:id/edit(.:format)
-
# sekret_comment GET /comments/:id(.:format)
-
# sekret_comment PATCH/PUT /comments/:id(.:format)
-
# sekret_comment DELETE /comments/:id(.:format)
-
#
-
# [:format]
-
# Allows you to specify the default value for optional +format+
-
# segment or disable it by supplying +false+.
-
#
-
# === Examples
-
#
-
# # routes call <tt>Admin::PostsController</tt>
-
# resources :posts, module: "admin"
-
#
-
# # resource actions are at /admin/posts.
-
# resources :posts, path: "admin/posts"
-
1
def resources(*resources, &block)
-
options = resources.extract_options!.dup
-
-
if apply_common_behavior_for(:resources, resources, options, &block)
-
return self
-
end
-
-
resource_scope(:resources, Resource.new(resources.pop, options)) do
-
yield if block_given?
-
-
concerns(options[:concerns]) if options[:concerns]
-
-
collection do
-
get :index if parent_resource.actions.include?(:index)
-
post :create if parent_resource.actions.include?(:create)
-
end
-
-
new do
-
get :new
-
end if parent_resource.actions.include?(:new)
-
-
member do
-
get :edit if parent_resource.actions.include?(:edit)
-
get :show if parent_resource.actions.include?(:show)
-
if parent_resource.actions.include?(:update)
-
patch :update
-
put :update
-
end
-
delete :destroy if parent_resource.actions.include?(:destroy)
-
end
-
end
-
-
self
-
end
-
-
# To add a route to the collection:
-
#
-
# resources :photos do
-
# collection do
-
# get 'search'
-
# end
-
# end
-
#
-
# This will enable Rails to recognize paths such as <tt>/photos/search</tt>
-
# with GET, and route to the search action of +PhotosController+. It will also
-
# create the <tt>search_photos_url</tt> and <tt>search_photos_path</tt>
-
# route helpers.
-
1
def collection
-
unless resource_scope?
-
raise ArgumentError, "can't use collection outside resource(s) scope"
-
end
-
-
with_scope_level(:collection) do
-
scope(parent_resource.collection_scope) do
-
yield
-
end
-
end
-
end
-
-
# To add a member route, add a member block into the resource block:
-
#
-
# resources :photos do
-
# member do
-
# get 'preview'
-
# end
-
# end
-
#
-
# This will recognize <tt>/photos/1/preview</tt> with GET, and route to the
-
# preview action of +PhotosController+. It will also create the
-
# <tt>preview_photo_url</tt> and <tt>preview_photo_path</tt> helpers.
-
1
def member
-
unless resource_scope?
-
raise ArgumentError, "can't use member outside resource(s) scope"
-
end
-
-
with_scope_level(:member) do
-
scope(parent_resource.member_scope) do
-
yield
-
end
-
end
-
end
-
-
1
def new
-
unless resource_scope?
-
raise ArgumentError, "can't use new outside resource(s) scope"
-
end
-
-
with_scope_level(:new) do
-
scope(parent_resource.new_scope(action_path(:new))) do
-
yield
-
end
-
end
-
end
-
-
1
def nested
-
unless resource_scope?
-
raise ArgumentError, "can't use nested outside resource(s) scope"
-
end
-
-
with_scope_level(:nested) do
-
if shallow?
-
with_exclusive_scope do
-
if @scope[:shallow_path].blank?
-
scope(parent_resource.nested_scope, nested_options) { yield }
-
else
-
scope(@scope[:shallow_path], :as => @scope[:shallow_prefix]) do
-
scope(parent_resource.nested_scope, nested_options) { yield }
-
end
-
end
-
end
-
else
-
scope(parent_resource.nested_scope, nested_options) { yield }
-
end
-
end
-
end
-
-
# See ActionDispatch::Routing::Mapper::Scoping#namespace
-
1
def namespace(path, options = {})
-
if resource_scope?
-
nested { super }
-
else
-
super
-
end
-
end
-
-
1
def shallow
-
scope(:shallow => true, :shallow_path => @scope[:path]) do
-
yield
-
end
-
end
-
-
1
def shallow?
-
3
parent_resource.instance_of?(Resource) && @scope[:shallow]
-
end
-
-
# match 'path' => 'controller#action'
-
# match 'path', to: 'controller#action'
-
# match 'path', 'otherpath', on: :member, via: :get
-
1
def match(path, *rest)
-
3
if rest.empty? && Hash === path
-
1
options = path
-
2
path, to = options.find { |name, value| name.is_a?(String) }
-
1
options[:to] = to
-
1
options.delete(path)
-
1
paths = [path]
-
else
-
2
options = rest.pop || {}
-
2
paths = [path] + rest
-
end
-
-
3
options[:anchor] = true unless options.key?(:anchor)
-
-
3
if options[:on] && !VALID_ON_OPTIONS.include?(options[:on])
-
raise ArgumentError, "Unknown scope #{on.inspect} given to :on"
-
end
-
-
6
paths.each { |_path| decomposed_match(_path, options.dup) }
-
3
self
-
end
-
-
1
def decomposed_match(path, options) # :nodoc:
-
3
if on = options.delete(:on)
-
send(on) { decomposed_match(path, options) }
-
else
-
3
case @scope[:scope_level]
-
when :resources
-
nested { decomposed_match(path, options) }
-
when :resource
-
member { decomposed_match(path, options) }
-
else
-
3
add_route(path, options)
-
end
-
end
-
end
-
-
1
def add_route(action, options) # :nodoc:
-
3
path = path_for_action(action, options.delete(:path))
-
-
3
if action.to_s =~ /^[\w\/]+$/
-
1
options[:action] ||= action unless action.to_s.include?("/")
-
else
-
2
action = nil
-
end
-
-
3
if !options.fetch(:as, true)
-
options.delete(:as)
-
else
-
3
options[:as] = name_for_action(options[:as], action)
-
end
-
-
3
mapping = Mapping.new(@set, @scope, URI.parser.escape(path), options)
-
3
app, conditions, requirements, defaults, as, anchor = mapping.to_route
-
3
@set.add_route(app, conditions, requirements, defaults, as, anchor)
-
end
-
-
1
def root(options={})
-
if @scope[:scope_level] == :resources
-
with_scope_level(:root) do
-
scope(parent_resource.path) do
-
super(options)
-
end
-
end
-
else
-
super(options)
-
end
-
end
-
-
1
protected
-
-
1
def parent_resource #:nodoc:
-
6
@scope[:scope_level_resource]
-
end
-
-
1
def apply_common_behavior_for(method, resources, options, &block) #:nodoc:
-
if resources.length > 1
-
resources.each { |r| send(method, r, options, &block) }
-
return true
-
end
-
-
if resource_scope?
-
nested { send(method, resources.pop, options, &block) }
-
return true
-
end
-
-
options.keys.each do |k|
-
(options[:constraints] ||= {})[k] = options.delete(k) if options[k].is_a?(Regexp)
-
end
-
-
scope_options = options.slice!(*RESOURCE_OPTIONS)
-
unless scope_options.empty?
-
scope(scope_options) do
-
send(method, resources.pop, options, &block)
-
end
-
return true
-
end
-
-
unless action_options?(options)
-
options.merge!(scope_action_options) if scope_action_options?
-
end
-
-
false
-
end
-
-
1
def action_options?(options) #:nodoc:
-
options[:only] || options[:except]
-
end
-
-
1
def scope_action_options? #:nodoc:
-
@scope[:options] && (@scope[:options][:only] || @scope[:options][:except])
-
end
-
-
1
def scope_action_options #:nodoc:
-
@scope[:options].slice(:only, :except)
-
end
-
-
1
def resource_scope? #:nodoc:
-
[:resource, :resources].include? @scope[:scope_level]
-
end
-
-
1
def resource_method_scope? #:nodoc:
-
1
[:collection, :member, :new].include? @scope[:scope_level]
-
end
-
-
1
def with_exclusive_scope
-
begin
-
old_name_prefix, old_path = @scope[:as], @scope[:path]
-
@scope[:as], @scope[:path] = nil, nil
-
-
with_scope_level(:exclusive) do
-
yield
-
end
-
ensure
-
@scope[:as], @scope[:path] = old_name_prefix, old_path
-
end
-
end
-
-
1
def with_scope_level(kind, resource = parent_resource)
-
old, @scope[:scope_level] = @scope[:scope_level], kind
-
old_resource, @scope[:scope_level_resource] = @scope[:scope_level_resource], resource
-
yield
-
ensure
-
@scope[:scope_level] = old
-
@scope[:scope_level_resource] = old_resource
-
end
-
-
1
def resource_scope(kind, resource) #:nodoc:
-
with_scope_level(kind, resource) do
-
scope(parent_resource.resource_scope) do
-
yield
-
end
-
end
-
end
-
-
1
def nested_options #:nodoc:
-
options = { :as => parent_resource.member_name }
-
options[:constraints] = {
-
parent_resource.nested_param => param_constraint
-
} if param_constraint?
-
-
options
-
end
-
-
1
def param_constraint? #:nodoc:
-
@scope[:constraints] && @scope[:constraints][parent_resource.param].is_a?(Regexp)
-
end
-
-
1
def param_constraint #:nodoc:
-
@scope[:constraints][parent_resource.param]
-
end
-
-
1
def canonical_action?(action, flag) #:nodoc:
-
5
flag && resource_method_scope? && CANONICAL_ACTIONS.include?(action.to_s)
-
end
-
-
1
def shallow_scoping? #:nodoc:
-
3
shallow? && @scope[:scope_level] == :member
-
end
-
-
1
def path_for_action(action, path) #:nodoc:
-
3
prefix = shallow_scoping? ?
-
"#{@scope[:shallow_path]}/#{parent_resource.shallow_scope}" : @scope[:path]
-
-
3
if canonical_action?(action, path.blank?)
-
prefix.to_s
-
else
-
3
"#{prefix}/#{action_path(action, path)}"
-
end
-
end
-
-
1
def action_path(name, path = nil) #:nodoc:
-
3
name = name.to_sym if name.is_a?(String)
-
3
path || @scope[:path_names][name] || name.to_s
-
end
-
-
1
def prefix_name_for_action(as, action) #:nodoc:
-
3
if as
-
1
as.to_s
-
2
elsif !canonical_action?(action, @scope[:scope_level])
-
2
action.to_s
-
end
-
end
-
-
1
def name_for_action(as, action) #:nodoc:
-
3
prefix = prefix_name_for_action(as, action)
-
3
prefix = Mapper.normalize_name(prefix) if prefix
-
3
name_prefix = @scope[:as]
-
-
3
if parent_resource
-
return nil unless as || action
-
-
collection_name = parent_resource.collection_name
-
member_name = parent_resource.member_name
-
end
-
-
3
name = case @scope[:scope_level]
-
when :nested
-
[name_prefix, prefix]
-
when :collection
-
[prefix, name_prefix, collection_name]
-
when :new
-
[prefix, :new, name_prefix, member_name]
-
when :member
-
[prefix, shallow_scoping? ? @scope[:shallow_prefix] : name_prefix, member_name]
-
when :root
-
[name_prefix, collection_name, prefix]
-
else
-
3
[name_prefix, member_name, prefix]
-
end
-
-
3
if candidate = name.select(&:present?).join("_").presence
-
# If a name was not explicitly given, we check if it is valid
-
# and return nil in case it isn't. Otherwise, we pass the invalid name
-
# forward so the underlying router engine treats it and raises an exception.
-
1
if as.nil?
-
candidate unless @set.routes.find { |r| r.name == candidate } || candidate !~ /\A[_a-z]/i
-
else
-
1
candidate
-
end
-
end
-
end
-
end
-
-
# Routing Concerns allow you to declare common routes that can be reused
-
# inside others resources and routes.
-
#
-
# concern :commentable do
-
# resources :comments
-
# end
-
#
-
# concern :image_attachable do
-
# resources :images, only: :index
-
# end
-
#
-
# These concerns are used in Resources routing:
-
#
-
# resources :messages, concerns: [:commentable, :image_attachable]
-
#
-
# or in a scope or namespace:
-
#
-
# namespace :posts do
-
# concerns :commentable
-
# end
-
1
module Concerns
-
# Define a routing concern using a name.
-
#
-
# Concerns may be defined inline, using a block, or handled by
-
# another object, by passing that object as the second parameter.
-
#
-
# The concern object, if supplied, should respond to <tt>call</tt>,
-
# which will receive two parameters:
-
#
-
# * The current mapper
-
# * A hash of options which the concern object may use
-
#
-
# Options may also be used by concerns defined in a block by accepting
-
# a block parameter. So, using a block, you might do something as
-
# simple as limit the actions available on certain resources, passing
-
# standard resource options through the concern:
-
#
-
# concern :commentable do |options|
-
# resources :comments, options
-
# end
-
#
-
# resources :posts, concerns: :commentable
-
# resources :archived_posts do
-
# # Don't allow comments on archived posts
-
# concerns :commentable, only: [:index, :show]
-
# end
-
#
-
# Or, using a callable object, you might implement something more
-
# specific to your application, which would be out of place in your
-
# routes file.
-
#
-
# # purchasable.rb
-
# class Purchasable
-
# def initialize(defaults = {})
-
# @defaults = defaults
-
# end
-
#
-
# def call(mapper, options = {})
-
# options = @defaults.merge(options)
-
# mapper.resources :purchases
-
# mapper.resources :receipts
-
# mapper.resources :returns if options[:returnable]
-
# end
-
# end
-
#
-
# # routes.rb
-
# concern :purchasable, Purchasable.new(returnable: true)
-
#
-
# resources :toys, concerns: :purchasable
-
# resources :electronics, concerns: :purchasable
-
# resources :pets do
-
# concerns :purchasable, returnable: false
-
# end
-
#
-
# Any routing helpers can be used inside a concern. If using a
-
# callable, they're accessible from the Mapper that's passed to
-
# <tt>call</tt>.
-
1
def concern(name, callable = nil, &block)
-
callable ||= lambda { |mapper, options| mapper.instance_exec(options, &block) }
-
@concerns[name] = callable
-
end
-
-
# Use the named concerns
-
#
-
# resources :posts do
-
# concerns :commentable
-
# end
-
#
-
# concerns also work in any routes helper that you want to use:
-
#
-
# namespace :posts do
-
# concerns :commentable
-
# end
-
1
def concerns(*args)
-
options = args.extract_options!
-
args.flatten.each do |name|
-
if concern = @concerns[name]
-
concern.call(self, options)
-
else
-
raise ArgumentError, "No concern named #{name} was found!"
-
end
-
end
-
end
-
end
-
-
1
def initialize(set) #:nodoc:
-
2
@set = set
-
2
@scope = { :path_names => @set.resources_path_names }
-
2
@concerns = {}
-
end
-
-
1
include Base
-
1
include HttpHelpers
-
1
include Redirection
-
1
include Scoping
-
1
include Concerns
-
1
include Resources
-
end
-
end
-
end
-
1
require 'action_controller/model_naming'
-
-
1
module ActionDispatch
-
1
module Routing
-
# Polymorphic URL helpers are methods for smart resolution to a named route call when
-
# given an Active Record model instance. They are to be used in combination with
-
# ActionController::Resources.
-
#
-
# These methods are useful when you want to generate correct URL or path to a RESTful
-
# resource without having to know the exact type of the record in question.
-
#
-
# Nested resources and/or namespaces are also supported, as illustrated in the example:
-
#
-
# polymorphic_url([:admin, @article, @comment])
-
#
-
# results in:
-
#
-
# admin_article_comment_url(@article, @comment)
-
#
-
# == Usage within the framework
-
#
-
# Polymorphic URL helpers are used in a number of places throughout the \Rails framework:
-
#
-
# * <tt>url_for</tt>, so you can use it with a record as the argument, e.g.
-
# <tt>url_for(@article)</tt>;
-
# * ActionView::Helpers::FormHelper uses <tt>polymorphic_path</tt>, so you can write
-
# <tt>form_for(@article)</tt> without having to specify <tt>:url</tt> parameter for the form
-
# action;
-
# * <tt>redirect_to</tt> (which, in fact, uses <tt>url_for</tt>) so you can write
-
# <tt>redirect_to(post)</tt> in your controllers;
-
# * ActionView::Helpers::AtomFeedHelper, so you don't have to explicitly specify URLs
-
# for feed entries.
-
#
-
# == Prefixed polymorphic helpers
-
#
-
# In addition to <tt>polymorphic_url</tt> and <tt>polymorphic_path</tt> methods, a
-
# number of prefixed helpers are available as a shorthand to <tt>action: "..."</tt>
-
# in options. Those are:
-
#
-
# * <tt>edit_polymorphic_url</tt>, <tt>edit_polymorphic_path</tt>
-
# * <tt>new_polymorphic_url</tt>, <tt>new_polymorphic_path</tt>
-
#
-
# Example usage:
-
#
-
# edit_polymorphic_path(@post) # => "/posts/1/edit"
-
# polymorphic_path(@post, format: :pdf) # => "/posts/1.pdf"
-
#
-
# == Usage with mounted engines
-
#
-
# If you are using a mounted engine and you need to use a polymorphic_url
-
# pointing at the engine's routes, pass in the engine's route proxy as the first
-
# argument to the method. For example:
-
#
-
# polymorphic_url([blog, @post]) # calls blog.post_path(@post)
-
# form_for([blog, @post]) # => "/blog/posts/1"
-
#
-
1
module PolymorphicRoutes
-
1
include ActionController::ModelNaming
-
-
# Constructs a call to a named RESTful route for the given record and returns the
-
# resulting URL string. For example:
-
#
-
# # calls post_url(post)
-
# polymorphic_url(post) # => "http://example.com/posts/1"
-
# polymorphic_url([blog, post]) # => "http://example.com/blogs/1/posts/1"
-
# polymorphic_url([:admin, blog, post]) # => "http://example.com/admin/blogs/1/posts/1"
-
# polymorphic_url([user, :blog, post]) # => "http://example.com/users/1/blog/posts/1"
-
# polymorphic_url(Comment) # => "http://example.com/comments"
-
#
-
# ==== Options
-
#
-
# * <tt>:action</tt> - Specifies the action prefix for the named route:
-
# <tt>:new</tt> or <tt>:edit</tt>. Default is no prefix.
-
# * <tt>:routing_type</tt> - Allowed values are <tt>:path</tt> or <tt>:url</tt>.
-
# Default is <tt>:url</tt>.
-
#
-
# ==== Examples
-
#
-
# # an Article record
-
# polymorphic_url(record) # same as article_url(record)
-
#
-
# # a Comment record
-
# polymorphic_url(record) # same as comment_url(record)
-
#
-
# # it recognizes new records and maps to the collection
-
# record = Comment.new
-
# polymorphic_url(record) # same as comments_url()
-
#
-
# # the class of a record will also map to the collection
-
# polymorphic_url(Comment) # same as comments_url()
-
#
-
1
def polymorphic_url(record_or_hash_or_array, options = {})
-
if record_or_hash_or_array.kind_of?(Array)
-
record_or_hash_or_array = record_or_hash_or_array.compact
-
if record_or_hash_or_array.first.is_a?(ActionDispatch::Routing::RoutesProxy)
-
proxy = record_or_hash_or_array.shift
-
end
-
record_or_hash_or_array = record_or_hash_or_array[0] if record_or_hash_or_array.size == 1
-
end
-
-
record = extract_record(record_or_hash_or_array)
-
record = convert_to_model(record)
-
-
args = Array === record_or_hash_or_array ?
-
record_or_hash_or_array.dup :
-
[ record_or_hash_or_array ]
-
-
inflection = if options[:action] && options[:action].to_s == "new"
-
args.pop
-
:singular
-
elsif (record.respond_to?(:persisted?) && !record.persisted?)
-
args.pop
-
:plural
-
elsif record.is_a?(Class)
-
args.pop
-
:plural
-
else
-
:singular
-
end
-
-
args.delete_if {|arg| arg.is_a?(Symbol) || arg.is_a?(String)}
-
named_route = build_named_route_call(record_or_hash_or_array, inflection, options)
-
-
url_options = options.except(:action, :routing_type)
-
unless url_options.empty?
-
args.last.kind_of?(Hash) ? args.last.merge!(url_options) : args << url_options
-
end
-
-
args.collect! { |a| convert_to_model(a) }
-
-
(proxy || self).send(named_route, *args)
-
end
-
-
# Returns the path component of a URL for the given record. It uses
-
# <tt>polymorphic_url</tt> with <tt>routing_type: :path</tt>.
-
1
def polymorphic_path(record_or_hash_or_array, options = {})
-
polymorphic_url(record_or_hash_or_array, options.merge(:routing_type => :path))
-
end
-
-
1
%w(edit new).each do |action|
-
2
module_eval <<-EOT, __FILE__, __LINE__ + 1
-
def #{action}_polymorphic_url(record_or_hash, options = {}) # def edit_polymorphic_url(record_or_hash, options = {})
-
polymorphic_url( # polymorphic_url(
-
record_or_hash, # record_or_hash,
-
options.merge(:action => "#{action}")) # options.merge(:action => "edit"))
-
end # end
-
#
-
def #{action}_polymorphic_path(record_or_hash, options = {}) # def edit_polymorphic_path(record_or_hash, options = {})
-
polymorphic_url( # polymorphic_url(
-
record_or_hash, # record_or_hash,
-
options.merge(:action => "#{action}", :routing_type => :path)) # options.merge(:action => "edit", :routing_type => :path))
-
end # end
-
EOT
-
end
-
-
1
private
-
1
def action_prefix(options)
-
options[:action] ? "#{options[:action]}_" : ''
-
end
-
-
1
def routing_type(options)
-
options[:routing_type] || :url
-
end
-
-
1
def build_named_route_call(records, inflection, options = {})
-
if records.is_a?(Array)
-
record = records.pop
-
route = records.map do |parent|
-
if parent.is_a?(Symbol) || parent.is_a?(String)
-
parent
-
else
-
model_name_from_record_or_class(parent).singular_route_key
-
end
-
end
-
else
-
record = extract_record(records)
-
route = []
-
end
-
-
if record.is_a?(Symbol) || record.is_a?(String)
-
route << record
-
elsif record
-
if inflection == :singular
-
route << model_name_from_record_or_class(record).singular_route_key
-
else
-
route << model_name_from_record_or_class(record).route_key
-
end
-
else
-
raise ArgumentError, "Nil location provided. Can't build URI."
-
end
-
-
route << routing_type(options)
-
-
action_prefix(options) + route.join("_")
-
end
-
-
1
def extract_record(record_or_hash_or_array)
-
case record_or_hash_or_array
-
when Array; record_or_hash_or_array.last
-
when Hash; record_or_hash_or_array[:id]
-
else record_or_hash_or_array
-
end
-
end
-
end
-
end
-
end
-
-
1
require 'action_dispatch/http/request'
-
1
require 'active_support/core_ext/uri'
-
1
require 'active_support/core_ext/array/extract_options'
-
1
require 'rack/utils'
-
1
require 'action_controller/metal/exceptions'
-
-
1
module ActionDispatch
-
1
module Routing
-
1
class Redirect # :nodoc:
-
1
attr_reader :status, :block
-
-
1
def initialize(status, block)
-
@status = status
-
@block = block
-
end
-
-
1
def call(env)
-
req = Request.new(env)
-
-
# If any of the path parameters has a invalid encoding then
-
# raise since it's likely to trigger errors further on.
-
req.symbolized_path_parameters.each do |key, value|
-
unless value.valid_encoding?
-
raise ActionController::BadRequest, "Invalid parameter: #{key} => #{value}"
-
end
-
end
-
-
uri = URI.parse(path(req.symbolized_path_parameters, req))
-
uri.scheme ||= req.scheme
-
uri.host ||= req.host
-
uri.port ||= req.port unless req.standard_port?
-
-
body = %(<html><body>You are being <a href="#{ERB::Util.h(uri.to_s)}">redirected</a>.</body></html>)
-
-
headers = {
-
'Location' => uri.to_s,
-
'Content-Type' => 'text/html',
-
'Content-Length' => body.length.to_s
-
}
-
-
[ status, headers, [body] ]
-
end
-
-
1
def path(params, request)
-
block.call params, request
-
end
-
-
1
def inspect
-
"redirect(#{status})"
-
end
-
end
-
-
1
class PathRedirect < Redirect
-
1
def path(params, request)
-
(params.empty? || !block.match(/%\{\w*\}/)) ? block : (block % escape(params))
-
end
-
-
1
def inspect
-
"redirect(#{status}, #{block})"
-
end
-
-
1
private
-
1
def escape(params)
-
Hash[params.map{ |k,v| [k, Rack::Utils.escape(v)] }]
-
end
-
end
-
-
1
class OptionRedirect < Redirect # :nodoc:
-
1
alias :options :block
-
-
1
def path(params, request)
-
url_options = {
-
:protocol => request.protocol,
-
:host => request.host,
-
:port => request.optional_port,
-
:path => request.path,
-
:params => request.query_parameters
-
}.merge options
-
-
if !params.empty? && url_options[:path].match(/%\{\w*\}/)
-
url_options[:path] = (url_options[:path] % escape_path(params))
-
end
-
-
ActionDispatch::Http::URL.url_for url_options
-
end
-
-
1
def inspect
-
"redirect(#{status}, #{options.map{ |k,v| "#{k}: #{v}" }.join(', ')})"
-
end
-
-
1
private
-
1
def escape_path(params)
-
Hash[params.map{ |k,v| [k, URI.parser.escape(v)] }]
-
end
-
end
-
-
1
module Redirection
-
-
# Redirect any path to another path:
-
#
-
# get "/stories" => redirect("/posts")
-
#
-
# You can also use interpolation in the supplied redirect argument:
-
#
-
# get 'docs/:article', to: redirect('/wiki/%{article}')
-
#
-
# Alternatively you can use one of the other syntaxes:
-
#
-
# The block version of redirect allows for the easy encapsulation of any logic associated with
-
# the redirect in question. Either the params and request are supplied as arguments, or just
-
# params, depending of how many arguments your block accepts. A string is required as a
-
# return value.
-
#
-
# get 'jokes/:number', to: redirect { |params, request|
-
# path = (params[:number].to_i.even? ? "wheres-the-beef" : "i-love-lamp")
-
# "http://#{request.host_with_port}/#{path}"
-
# }
-
#
-
# Note that the +do end+ syntax for the redirect block wouldn't work, as Ruby would pass
-
# the block to +get+ instead of +redirect+. Use <tt>{ ... }</tt> instead.
-
#
-
# The options version of redirect allows you to supply only the parts of the url which need
-
# to change, it also supports interpolation of the path similar to the first example.
-
#
-
# get 'stores/:name', to: redirect(subdomain: 'stores', path: '/%{name}')
-
# get 'stores/:name(*all)', to: redirect(subdomain: 'stores', path: '/%{name}%{all}')
-
#
-
# Finally, an object which responds to call can be supplied to redirect, allowing you to reuse
-
# common redirect routes. The call method must accept two arguments, params and request, and return
-
# a string.
-
#
-
# get 'accounts/:name' => redirect(SubdomainRedirector.new('api'))
-
#
-
1
def redirect(*args, &block)
-
options = args.extract_options!
-
status = options.delete(:status) || 301
-
path = args.shift
-
-
return OptionRedirect.new(status, options) if options.any?
-
return PathRedirect.new(status, path) if String === path
-
-
block = path if path.respond_to? :call
-
raise ArgumentError, "redirection argument not supported" unless block
-
Redirect.new status, block
-
end
-
end
-
end
-
end
-
1
require 'journey'
-
1
require 'forwardable'
-
1
require 'active_support/core_ext/object/to_query'
-
1
require 'active_support/core_ext/hash/slice'
-
1
require 'active_support/core_ext/module/remove_method'
-
1
require 'action_controller/metal/exceptions'
-
-
1
module ActionDispatch
-
1
module Routing
-
1
class RouteSet #:nodoc:
-
# Since the router holds references to many parts of the system
-
# like engines, controllers and the application itself, inspecting
-
# the route set can actually be really slow, therefore we default
-
# alias inspect to to_s.
-
1
alias inspect to_s
-
-
1
PARAMETERS_KEY = 'action_dispatch.request.path_parameters'
-
-
1
class Dispatcher #:nodoc:
-
1
def initialize(options={})
-
3
@defaults = options[:defaults]
-
3
@glob_param = options.delete(:glob)
-
3
@controllers = {}
-
end
-
-
1
def call(env)
-
1
params = env[PARAMETERS_KEY]
-
-
# If any of the path parameters has a invalid encoding then
-
# raise since it's likely to trigger errors further on.
-
1
params.each do |key, value|
-
2
unless value.valid_encoding?
-
raise ActionController::BadRequest, "Invalid parameter: #{key} => #{value}"
-
end
-
end
-
-
1
prepare_params!(params)
-
-
# Just raise undefined constant errors if a controller was specified as default.
-
1
unless controller = controller(params, @defaults.key?(:controller))
-
return [404, {'X-Cascade' => 'pass'}, []]
-
end
-
-
1
dispatch(controller, params[:action], env)
-
end
-
-
1
def prepare_params!(params)
-
1
normalize_controller!(params)
-
1
merge_default_action!(params)
-
1
split_glob_param!(params) if @glob_param
-
end
-
-
# If this is a default_controller (i.e. a controller specified by the user)
-
# we should raise an error in case it's not found, because it usually means
-
# a user error. However, if the controller was retrieved through a dynamic
-
# segment, as in :controller(/:action), we should simply return nil and
-
# delegate the control back to Rack cascade. Besides, if this is not a default
-
# controller, it means we should respect the @scope[:module] parameter.
-
1
def controller(params, default_controller=true)
-
1
if params && params.key?(:controller)
-
1
controller_param = params[:controller]
-
1
controller_reference(controller_param)
-
end
-
rescue NameError => e
-
raise ActionController::RoutingError, e.message, e.backtrace if default_controller
-
end
-
-
1
private
-
-
1
def controller_reference(controller_param)
-
1
controller_name = "#{controller_param.camelize}Controller"
-
-
1
unless controller = @controllers[controller_param]
-
1
controller = @controllers[controller_param] =
-
ActiveSupport::Dependencies.reference(controller_name)
-
end
-
1
controller.get(controller_name)
-
end
-
-
1
def dispatch(controller, action, env)
-
1
controller.action(action).call(env)
-
end
-
-
1
def normalize_controller!(params)
-
1
params[:controller] = params[:controller].underscore if params.key?(:controller)
-
end
-
-
1
def merge_default_action!(params)
-
1
params[:action] ||= 'index'
-
end
-
-
1
def split_glob_param!(params)
-
params[@glob_param] = params[@glob_param].split('/').map { |v| URI.parser.unescape(v) }
-
end
-
end
-
-
# A NamedRouteCollection instance is a collection of named routes, and also
-
# maintains an anonymous module that can be used to install helpers for the
-
# named routes.
-
1
class NamedRouteCollection #:nodoc:
-
1
include Enumerable
-
1
attr_reader :routes, :helpers, :module
-
-
1
def initialize
-
2
@routes = {}
-
2
@helpers = []
-
2
@module = Module.new do
-
2
protected
-
-
2
def handle_positional_args(args, options, segment_keys)
-
2
inner_options = args.extract_options!
-
2
result = options.dup
-
-
2
if args.size > 0
-
keys = segment_keys
-
if args.size < keys.size - 1 # take format into account
-
keys -= self.url_options.keys if self.respond_to?(:url_options)
-
keys -= options.keys
-
end
-
result.merge!(Hash[keys.zip(args)])
-
end
-
-
2
result.merge!(inner_options)
-
end
-
end
-
end
-
-
1
def helper_names
-
self.module.instance_methods.map(&:to_s)
-
end
-
-
1
def clear!
-
2
@routes.clear
-
2
@helpers.clear
-
end
-
-
1
def add(name, route)
-
1
routes[name.to_sym] = route
-
1
define_named_route_methods(name, route)
-
end
-
-
1
def get(name)
-
1
routes[name.to_sym]
-
end
-
-
1
alias []= add
-
1
alias [] get
-
1
alias clear clear!
-
-
1
def each
-
routes.each { |name, route| yield name, route }
-
self
-
end
-
-
1
def names
-
routes.keys
-
end
-
-
1
def length
-
routes.length
-
end
-
-
1
private
-
-
1
def define_named_route_methods(name, route)
-
1
define_url_helper route, :"#{name}_path",
-
route.defaults.merge(:use_route => name, :only_path => true)
-
1
define_url_helper route, :"#{name}_url",
-
route.defaults.merge(:use_route => name, :only_path => false)
-
end
-
-
# Create a url helper allowing ordered parameters to be associated
-
# with corresponding dynamic segments, so you can do:
-
#
-
# foo_url(bar, baz, bang)
-
#
-
# Instead of:
-
#
-
# foo_url(bar: bar, baz: baz, bang: bang)
-
#
-
# Also allow options hash, so you can do:
-
#
-
# foo_url(bar, baz, bang, sort_by: 'baz')
-
#
-
1
def define_url_helper(route, name, options)
-
2
@module.module_eval <<-END_EVAL, __FILE__, __LINE__ + 1
-
remove_possible_method :#{name}
-
def #{name}(*args)
-
if #{optimize_helper?(route)} && args.size == #{route.required_parts.size} && !args.last.is_a?(Hash) && optimize_routes_generation?
-
options = #{options.inspect}
-
options.merge!(url_options) if respond_to?(:url_options)
-
options[:path] = "#{optimized_helper(route)}"
-
ActionDispatch::Http::URL.url_for(options)
-
else
-
url_for(handle_positional_args(args, #{options.inspect}, #{route.segment_keys.inspect}))
-
end
-
end
-
END_EVAL
-
-
2
helpers << name
-
end
-
-
# Clause check about when we need to generate an optimized helper.
-
1
def optimize_helper?(route) #:nodoc:
-
2
route.requirements.except(:controller, :action).empty?
-
end
-
-
# Generates the interpolation to be used in the optimized helper.
-
1
def optimized_helper(route)
-
2
string_route = route.ast.to_s
-
-
2
while string_route.gsub!(/\([^\)]*\)/, "")
-
2
true
-
end
-
-
2
route.required_parts.each_with_index do |part, i|
-
# Replace each route parameter
-
# e.g. :id for regular parameter or *path for globbing
-
# with ruby string interpolation code
-
string_route.gsub!(/(\*|:)#{part}/, "\#{Journey::Router::Utils.escape_fragment(args[#{i}].to_param)}")
-
end
-
-
2
string_route
-
end
-
end
-
-
1
attr_accessor :formatter, :set, :named_routes, :default_scope, :router
-
1
attr_accessor :disable_clear_and_finalize, :resources_path_names
-
1
attr_accessor :default_url_options, :request_class
-
-
1
alias :routes :set
-
-
1
def self.default_resources_path_names
-
2
{ :new => 'new', :edit => 'edit' }
-
end
-
-
1
def initialize(request_class = ActionDispatch::Request)
-
2
self.named_routes = NamedRouteCollection.new
-
2
self.resources_path_names = self.class.default_resources_path_names.dup
-
2
self.default_url_options = {}
-
2
self.request_class = request_class
-
-
2
@append = []
-
2
@prepend = []
-
2
@disable_clear_and_finalize = false
-
2
@finalized = false
-
-
2
@set = Journey::Routes.new
-
2
@router = Journey::Router.new(@set, {
-
:parameters_key => PARAMETERS_KEY,
-
:request_class => request_class})
-
2
@formatter = Journey::Formatter.new @set
-
end
-
-
1
def draw(&block)
-
2
clear! unless @disable_clear_and_finalize
-
2
eval_block(block)
-
2
finalize! unless @disable_clear_and_finalize
-
nil
-
end
-
-
1
def append(&block)
-
@append << block
-
end
-
-
1
def prepend(&block)
-
@prepend << block
-
end
-
-
1
def eval_block(block)
-
2
if block.arity == 1
-
raise "You are using the old router DSL which has been removed in Rails 3.1. " <<
-
"Please check how to update your routes file at: http://www.engineyard.com/blog/2010/the-lowdown-on-routes-in-rails-3/"
-
end
-
2
mapper = Mapper.new(self)
-
2
if default_scope
-
mapper.with_default_scope(default_scope, &block)
-
else
-
2
mapper.instance_exec(&block)
-
end
-
end
-
-
1
def finalize!
-
2
return if @finalized
-
2
@append.each { |blk| eval_block(blk) }
-
2
@finalized = true
-
end
-
-
1
def clear!
-
2
@finalized = false
-
2
@url_helpers = nil
-
2
named_routes.clear
-
2
set.clear
-
2
formatter.clear
-
2
@prepend.each { |blk| eval_block(blk) }
-
end
-
-
1
module MountedHelpers #:nodoc:
-
1
extend ActiveSupport::Concern
-
1
include UrlFor
-
end
-
-
# Contains all the mounted helpers accross different
-
# engines and the `main_app` helper for the application.
-
# You can include this in your classes if you want to
-
# access routes for other engines.
-
1
def mounted_helpers
-
16
MountedHelpers
-
end
-
-
1
def define_mounted_helper(name)
-
return if MountedHelpers.method_defined?(name)
-
-
routes = self
-
MountedHelpers.class_eval do
-
define_method "_#{name}" do
-
RoutesProxy.new(routes, _routes_context)
-
end
-
end
-
-
MountedHelpers.class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
-
def #{name}
-
@_#{name} ||= _#{name}
-
end
-
RUBY
-
end
-
-
1
def url_helpers
-
@url_helpers ||= begin
-
2
routes = self
-
-
2
Module.new do
-
2
extend ActiveSupport::Concern
-
2
include UrlFor
-
-
# Define url_for in the singleton level so one can do:
-
# Rails.application.routes.url_helpers.url_for(args)
-
2
@_routes = routes
-
2
class << self
-
2
delegate :url_for, :optimize_routes_generation?, :to => '@_routes'
-
end
-
-
# Make named_routes available in the module singleton
-
# as well, so one can do:
-
# Rails.application.routes.url_helpers.posts_path
-
2
extend routes.named_routes.module
-
-
# Any class that includes this module will get all
-
# named routes...
-
2
include routes.named_routes.module
-
-
# plus a singleton class method called _routes ...
-
2
included do
-
33
singleton_class.send(:redefine_method, :_routes) { routes }
-
end
-
-
# And an instance method _routes. Note that
-
# UrlFor (included in this module) add extra
-
# conveniences for working with @_routes.
-
8
define_method(:_routes) { @_routes || routes }
-
end
-
17
end
-
end
-
-
1
def empty?
-
routes.empty?
-
end
-
-
1
def add_route(app, conditions = {}, requirements = {}, defaults = {}, name = nil, anchor = true)
-
3
raise ArgumentError, "Invalid route name: '#{name}'" unless name.blank? || name.to_s.match(/^[_a-z]\w*$/i)
-
-
3
path = build_path(conditions.delete(:path_info), requirements, SEPARATORS, anchor)
-
12
conditions = build_conditions(conditions, path.names.map { |x| x.to_sym })
-
-
3
route = @set.add_route(app, path, conditions, defaults, name)
-
3
named_routes[name] = route if name && !named_routes[name]
-
3
route
-
end
-
-
1
def build_path(path, requirements, separators, anchor)
-
3
strexp = Journey::Router::Strexp.new(
-
path,
-
requirements,
-
SEPARATORS,
-
anchor)
-
-
3
pattern = Journey::Path::Pattern.new(strexp)
-
-
3
builder = Journey::GTG::Builder.new pattern.spec
-
-
# Get all the symbol nodes followed by literals that are not the
-
# dummy node.
-
3
symbols = pattern.spec.grep(Journey::Nodes::Symbol).find_all { |n|
-
9
builder.followpos(n).first.literal?
-
}
-
-
# Get all the symbol nodes preceded by literals.
-
3
symbols.concat pattern.spec.find_all(&:literal?).map { |n|
-
1
builder.followpos(n).first
-
}.find_all(&:symbol?)
-
-
3
symbols.each { |x|
-
x.regexp = /(?:#{Regexp.union(x.regexp, '-')})+/
-
}
-
-
3
pattern
-
end
-
1
private :build_path
-
-
1
def build_conditions(current_conditions, path_values)
-
3
conditions = current_conditions.dup
-
-
# Rack-Mount requires that :request_method be a regular expression.
-
# :request_method represents the HTTP verb that matches this route.
-
#
-
# Here we munge values before they get sent on to rack-mount.
-
3
verbs = conditions[:request_method] || []
-
3
unless verbs.empty?
-
3
conditions[:request_method] = %r[^#{verbs.join('|')}$]
-
end
-
-
3
conditions.keep_if do |k, _|
-
3
k == :action || k == :controller ||
-
@request_class.public_method_defined?(k) || path_values.include?(k)
-
end
-
end
-
1
private :build_conditions
-
-
1
class Generator #:nodoc:
-
1
PARAMETERIZE = lambda do |name, value|
-
4
if name == :controller
-
2
value
-
elsif value.is_a?(Array)
-
value.map { |v| v.to_param }.join('/')
-
elsif param = value.to_param
-
2
param
-
end
-
end
-
-
1
attr_reader :options, :recall, :set, :named_route
-
-
1
def initialize(options, recall, set)
-
4
@named_route = options.delete(:use_route)
-
4
@options = options.dup
-
4
@recall = recall.dup
-
4
@set = set
-
-
4
normalize_options!
-
4
normalize_controller_action_id!
-
4
use_relative_controller!
-
4
normalize_controller!
-
4
handle_nil_action!
-
end
-
-
1
def controller
-
8
@options[:controller]
-
end
-
-
1
def current_controller
-
6
@recall[:controller]
-
end
-
-
1
def use_recall_for(key)
-
4
if @recall[key] && (!@options.key?(key) || @options[key] == @recall[key])
-
if !named_route_exists? || segment_keys.include?(key)
-
@options[key] = @recall.delete(key)
-
end
-
end
-
end
-
-
1
def normalize_options!
-
# If an explicit :controller was given, always make :action explicit
-
# too, so that action expiry works as expected for things like
-
#
-
# generate({controller: 'content'}, {controller: 'content', action: 'show'})
-
#
-
# (the above is from the unit tests). In the above case, because the
-
# controller was explicitly given, but no action, the action is implied to
-
# be "index", not the recalled action of "show".
-
-
4
if options[:controller]
-
4
options[:action] ||= 'index'
-
4
options[:controller] = options[:controller].to_s
-
end
-
-
4
if options[:action]
-
4
options[:action] = options[:action].to_s
-
end
-
end
-
-
# This pulls :controller, :action, and :id out of the recall.
-
# The recall key is only used if there is no key in the options
-
# or if the key in the options is identical. If any of
-
# :controller, :action or :id is not found, don't pull any
-
# more keys from the recall.
-
1
def normalize_controller_action_id!
-
4
@recall[:action] ||= 'index' if current_controller
-
-
4
use_recall_for(:controller) or return
-
use_recall_for(:action) or return
-
use_recall_for(:id)
-
end
-
-
# if the current controller is "foo/bar/baz" and controller: "baz/bat"
-
# is specified, the controller becomes "foo/baz/bat"
-
1
def use_relative_controller!
-
4
if !named_route && different_controller? && !controller.start_with?("/")
-
old_parts = current_controller.split('/')
-
size = controller.count("/") + 1
-
parts = old_parts[0...-size] << controller
-
@options[:controller] = parts.join("/")
-
end
-
end
-
-
# Remove leading slashes from controllers
-
1
def normalize_controller!
-
4
@options[:controller] = controller.sub(%r{^/}, '') if controller
-
end
-
-
# This handles the case of action: nil being explicitly passed.
-
# It is identical to action: "index"
-
1
def handle_nil_action!
-
4
if options.has_key?(:action) && options[:action].nil?
-
options[:action] = 'index'
-
end
-
4
recall[:action] = options.delete(:action) if options[:action] == 'index'
-
end
-
-
# Generates a path from routes, returns [path, params]
-
# if no path is returned the formatter will raise Journey::Router::RoutingError
-
1
def generate
-
4
@set.formatter.generate(:path_info, named_route, options, recall, PARAMETERIZE)
-
rescue Journey::Router::RoutingError => e
-
raise ActionController::UrlGenerationError, "No route matches #{options.inspect} #{e.message}"
-
end
-
-
1
def different_controller?
-
2
return false unless current_controller
-
controller.to_param != current_controller.to_param
-
end
-
-
1
private
-
1
def named_route_exists?
-
named_route && set.named_routes[named_route]
-
end
-
-
1
def segment_keys
-
set.named_routes[named_route].segment_keys
-
end
-
end
-
-
# Generate the path indicated by the arguments, and return an array of
-
# the keys that were not used to generate it.
-
1
def extra_keys(options, recall={})
-
generate_extras(options, recall).last
-
end
-
-
1
def generate_extras(options, recall={})
-
path, params = generate(options, recall)
-
return path, params.keys
-
end
-
-
1
def generate(options, recall = {})
-
4
Generator.new(options, recall, self).generate
-
end
-
-
1
RESERVED_OPTIONS = [:host, :protocol, :port, :subdomain, :domain, :tld_length,
-
:trailing_slash, :anchor, :params, :only_path, :script_name,
-
:original_script_name]
-
-
1
def mounted?
-
2
false
-
end
-
-
1
def optimize_routes_generation?
-
2
!mounted? && default_url_options.empty?
-
end
-
-
1
def _generate_prefix(options = {})
-
nil
-
end
-
-
# The +options+ argument must be +nil+ or a hash whose keys are *symbols*.
-
1
def url_for(options)
-
4
options = default_url_options.merge(options || {})
-
-
4
user, password = extract_authentication(options)
-
4
recall = options.delete(:_recall)
-
-
4
original_script_name = options.delete(:original_script_name).presence
-
4
script_name = options.delete(:script_name).presence || _generate_prefix(options)
-
-
4
if script_name && original_script_name
-
script_name = original_script_name + script_name
-
end
-
-
4
path_options = options.except(*RESERVED_OPTIONS)
-
4
path_options = yield(path_options) if block_given?
-
-
4
path, params = generate(path_options, recall || {})
-
4
params.merge!(options[:params] || {})
-
-
4
ActionDispatch::Http::URL.url_for(options.merge!({
-
:path => path,
-
:script_name => script_name,
-
:params => params,
-
:user => user,
-
:password => password
-
}))
-
end
-
-
1
def call(env)
-
1
@router.call(env)
-
end
-
-
1
def recognize_path(path, environment = {})
-
method = (environment[:method] || "GET").to_s.upcase
-
path = Journey::Router::Utils.normalize_path(path) unless path =~ %r{://}
-
extras = environment[:extras] || {}
-
-
begin
-
env = Rack::MockRequest.env_for(path, {:method => method})
-
rescue URI::InvalidURIError => e
-
raise ActionController::RoutingError, e.message
-
end
-
-
req = @request_class.new(env)
-
@router.recognize(req) do |route, matches, params|
-
params.merge!(extras)
-
params.each do |key, value|
-
if value.is_a?(String)
-
value = value.dup.force_encoding(Encoding::BINARY)
-
params[key] = URI.parser.unescape(value)
-
end
-
end
-
old_params = env[::ActionDispatch::Routing::RouteSet::PARAMETERS_KEY]
-
env[::ActionDispatch::Routing::RouteSet::PARAMETERS_KEY] = (old_params || {}).merge(params)
-
dispatcher = route.app
-
while dispatcher.is_a?(Mapper::Constraints) && dispatcher.matches?(env) do
-
dispatcher = dispatcher.app
-
end
-
-
if dispatcher.is_a?(Dispatcher)
-
if dispatcher.controller(params, false)
-
dispatcher.prepare_params!(params)
-
return params
-
else
-
raise ActionController::RoutingError, "A route matches #{path.inspect}, but references missing controller: #{params[:controller].camelize}Controller"
-
end
-
end
-
end
-
-
raise ActionController::RoutingError, "No route matches #{path.inspect}"
-
end
-
-
1
private
-
-
1
def extract_authentication(options)
-
4
if options[:user] && options[:password]
-
[options.delete(:user), options.delete(:password)]
-
else
-
nil
-
end
-
end
-
-
end
-
end
-
end
-
1
module ActionDispatch
-
1
module Routing
-
# In <tt>config/routes.rb</tt> you define URL-to-controller mappings, but the reverse
-
# is also possible: an URL can be generated from one of your routing definitions.
-
# URL generation functionality is centralized in this module.
-
#
-
# See ActionDispatch::Routing for general information about routing and routes.rb.
-
#
-
# <b>Tip:</b> If you need to generate URLs from your models or some other place,
-
# then ActionController::UrlFor is what you're looking for. Read on for
-
# an introduction. In general, this module should not be included on its own,
-
# as it is usually included by url_helpers (as in Rails.application.routes.url_helpers).
-
#
-
# == URL generation from parameters
-
#
-
# As you may know, some functions, such as ActionController::Base#url_for
-
# and ActionView::Helpers::UrlHelper#link_to, can generate URLs given a set
-
# of parameters. For example, you've probably had the chance to write code
-
# like this in one of your views:
-
#
-
# <%= link_to('Click here', controller: 'users',
-
# action: 'new', message: 'Welcome!') %>
-
# # => "/users/new?message=Welcome%21"
-
#
-
# link_to, and all other functions that require URL generation functionality,
-
# actually use ActionController::UrlFor under the hood. And in particular,
-
# they use the ActionController::UrlFor#url_for method. One can generate
-
# the same path as the above example by using the following code:
-
#
-
# include UrlFor
-
# url_for(controller: 'users',
-
# action: 'new',
-
# message: 'Welcome!',
-
# only_path: true)
-
# # => "/users/new?message=Welcome%21"
-
#
-
# Notice the <tt>only_path: true</tt> part. This is because UrlFor has no
-
# information about the website hostname that your Rails app is serving. So if you
-
# want to include the hostname as well, then you must also pass the <tt>:host</tt>
-
# argument:
-
#
-
# include UrlFor
-
# url_for(controller: 'users',
-
# action: 'new',
-
# message: 'Welcome!',
-
# host: 'www.example.com')
-
# # => "http://www.example.com/users/new?message=Welcome%21"
-
#
-
# By default, all controllers and views have access to a special version of url_for,
-
# that already knows what the current hostname is. So if you use url_for in your
-
# controllers or your views, then you don't need to explicitly pass the <tt>:host</tt>
-
# argument.
-
#
-
# For convenience reasons, mailers provide a shortcut for ActionController::UrlFor#url_for.
-
# So within mailers, you only have to type 'url_for' instead of 'ActionController::UrlFor#url_for'
-
# in full. However, mailers don't have hostname information, and that's why you'll still
-
# have to specify the <tt>:host</tt> argument when generating URLs in mailers.
-
#
-
#
-
# == URL generation for named routes
-
#
-
# UrlFor also allows one to access methods that have been auto-generated from
-
# named routes. For example, suppose that you have a 'users' resource in your
-
# <tt>config/routes.rb</tt>:
-
#
-
# resources :users
-
#
-
# This generates, among other things, the method <tt>users_path</tt>. By default,
-
# this method is accessible from your controllers, views and mailers. If you need
-
# to access this auto-generated method from other places (such as a model), then
-
# you can do that by including Rails.application.routes.url_helpers in your class:
-
#
-
# class User < ActiveRecord::Base
-
# include Rails.application.routes.url_helpers
-
#
-
# def base_uri
-
# user_path(self)
-
# end
-
# end
-
#
-
# User.find(1).base_uri # => "/users/1"
-
#
-
1
module UrlFor
-
1
extend ActiveSupport::Concern
-
1
include PolymorphicRoutes
-
-
1
included do
-
21
unless method_defined?(:default_url_options)
-
# Including in a class uses an inheritable hash. Modules get a plain hash.
-
20
if respond_to?(:class_attribute)
-
19
class_attribute :default_url_options
-
else
-
1
mattr_writer :default_url_options
-
end
-
-
20
self.default_url_options = {}
-
end
-
-
21
include(*_url_for_modules) if respond_to?(:_url_for_modules)
-
end
-
-
1
def initialize(*)
-
260
@_routes = nil
-
260
super
-
end
-
-
# Hook overridden in controller to add request information
-
# with `default_url_options`. Application logic should not
-
# go into url_options.
-
1
def url_options
-
4
default_url_options
-
end
-
-
# Generate a url based on the options provided, default_url_options and the
-
# routes defined in routes.rb. The following options are supported:
-
#
-
# * <tt>:only_path</tt> - If true, the relative url is returned. Defaults to +false+.
-
# * <tt>:protocol</tt> - The protocol to connect to. Defaults to 'http'.
-
# * <tt>:host</tt> - Specifies the host the link should be targeted at.
-
# If <tt>:only_path</tt> is false, this option must be
-
# provided either explicitly, or via +default_url_options+.
-
# * <tt>:subdomain</tt> - Specifies the subdomain of the link, using the +tld_length+
-
# to split the subdomain from the host.
-
# If false, removes all subdomains from the host part of the link.
-
# * <tt>:domain</tt> - Specifies the domain of the link, using the +tld_length+
-
# to split the domain from the host.
-
# * <tt>:tld_length</tt> - Number of labels the TLD id composed of, only used if
-
# <tt>:subdomain</tt> or <tt>:domain</tt> are supplied. Defaults to
-
# <tt>ActionDispatch::Http::URL.tld_length</tt>, which in turn defaults to 1.
-
# * <tt>:port</tt> - Optionally specify the port to connect to.
-
# * <tt>:anchor</tt> - An anchor name to be appended to the path.
-
# * <tt>:trailing_slash</tt> - If true, adds a trailing slash, as in "/archive/2009/"
-
#
-
# Any other key (<tt>:controller</tt>, <tt>:action</tt>, etc.) given to
-
# +url_for+ is forwarded to the Routes module.
-
#
-
# url_for controller: 'tasks', action: 'testing', host: 'somehost.org', port: '8080'
-
# # => 'http://somehost.org:8080/tasks/testing'
-
# url_for controller: 'tasks', action: 'testing', host: 'somehost.org', anchor: 'ok', only_path: true
-
# # => '/tasks/testing#ok'
-
# url_for controller: 'tasks', action: 'testing', trailing_slash: true
-
# # => 'http://somehost.org/tasks/testing/'
-
# url_for controller: 'tasks', action: 'testing', host: 'somehost.org', number: '33'
-
# # => 'http://somehost.org/tasks/testing?number=33'
-
1
def url_for(options = nil)
-
4
case options
-
when nil
-
_routes.url_for(url_options.symbolize_keys)
-
when Hash
-
4
_routes.url_for(options.symbolize_keys.reverse_merge!(url_options))
-
when String
-
options
-
else
-
polymorphic_url(options)
-
end
-
end
-
-
1
protected
-
-
1
def optimize_routes_generation?
-
2
return @_optimized_routes if defined?(@_optimized_routes)
-
2
@_optimized_routes = _routes.optimize_routes_generation? && default_url_options.empty?
-
end
-
-
1
def _with_routes(routes)
-
old_routes, @_routes = @_routes, routes
-
yield
-
ensure
-
@_routes = old_routes
-
end
-
-
1
def _routes_context
-
self
-
end
-
end
-
end
-
end
-
1
module ActionDispatch
-
1
module Assertions
-
1
autoload :DomAssertions, 'action_dispatch/testing/assertions/dom'
-
1
autoload :ResponseAssertions, 'action_dispatch/testing/assertions/response'
-
1
autoload :RoutingAssertions, 'action_dispatch/testing/assertions/routing'
-
1
autoload :SelectorAssertions, 'action_dispatch/testing/assertions/selector'
-
1
autoload :TagAssertions, 'action_dispatch/testing/assertions/tag'
-
-
1
extend ActiveSupport::Concern
-
-
1
include DomAssertions
-
1
include ResponseAssertions
-
1
include RoutingAssertions
-
1
include SelectorAssertions
-
1
include TagAssertions
-
end
-
end
-
-
1
require 'action_view/vendor/html-scanner'
-
-
1
module ActionDispatch
-
1
module Assertions
-
1
module DomAssertions
-
# \Test two HTML strings for equivalency (e.g., identical up to reordering of attributes)
-
#
-
# # assert that the referenced method generates the appropriate HTML string
-
# assert_dom_equal '<a href="http://www.example.com">Apples</a>', link_to("Apples", "http://www.example.com")
-
1
def assert_dom_equal(expected, actual, message = "")
-
expected_dom = HTML::Document.new(expected).root
-
actual_dom = HTML::Document.new(actual).root
-
assert_equal expected_dom, actual_dom
-
end
-
-
# The negated form of +assert_dom_equivalent+.
-
#
-
# # assert that the referenced method does not generate the specified HTML string
-
# assert_dom_not_equal '<a href="http://www.example.com">Apples</a>', link_to("Oranges", "http://www.example.com")
-
1
def assert_dom_not_equal(expected, actual, message = "")
-
expected_dom = HTML::Document.new(expected).root
-
actual_dom = HTML::Document.new(actual).root
-
refute_equal expected_dom, actual_dom
-
end
-
end
-
end
-
end
-
-
1
module ActionDispatch
-
1
module Assertions
-
# A small suite of assertions that test responses from \Rails applications.
-
1
module ResponseAssertions
-
# Asserts that the response is one of the following types:
-
#
-
# * <tt>:success</tt> - Status code was in the 200-299 range
-
# * <tt>:redirect</tt> - Status code was in the 300-399 range
-
# * <tt>:missing</tt> - Status code was 404
-
# * <tt>:error</tt> - Status code was in the 500-599 range
-
#
-
# You can also pass an explicit status number like <tt>assert_response(501)</tt>
-
# or its symbolic equivalent <tt>assert_response(:not_implemented)</tt>.
-
# See Rack::Utils::SYMBOL_TO_STATUS_CODE for a full list.
-
#
-
# # assert that the response was a redirection
-
# assert_response :redirect
-
#
-
# # assert that the response code was status code 401 (unauthorized)
-
# assert_response 401
-
1
def assert_response(type, message = nil)
-
message ||= "Expected response to be a <#{type}>, but was <#{@response.response_code}>"
-
-
if Symbol === type
-
if [:success, :missing, :redirect, :error].include?(type)
-
assert @response.send("#{type}?"), message
-
else
-
code = Rack::Utils::SYMBOL_TO_STATUS_CODE[type]
-
assert_equal code, @response.response_code, message
-
end
-
else
-
assert_equal type, @response.response_code, message
-
end
-
end
-
-
# Assert that the redirection options passed in match those of the redirect called in the latest action.
-
# This match can be partial, such that <tt>assert_redirected_to(controller: "weblog")</tt> will also
-
# match the redirection of <tt>redirect_to(controller: "weblog", action: "show")</tt> and so on.
-
#
-
# # assert that the redirection was to the "index" action on the WeblogController
-
# assert_redirected_to controller: "weblog", action: "index"
-
#
-
# # assert that the redirection was to the named route login_url
-
# assert_redirected_to login_url
-
#
-
# # assert that the redirection was to the url for @customer
-
# assert_redirected_to @customer
-
#
-
# # asserts that the redirection matches the regular expression
-
# assert_redirected_to %r(\Ahttp://example.org)
-
1
def assert_redirected_to(options = {}, message=nil)
-
assert_response(:redirect, message)
-
return true if options === @response.location
-
-
redirect_is = normalize_argument_to_redirection(@response.location)
-
redirect_expected = normalize_argument_to_redirection(options)
-
-
message ||= "Expected response to be a redirect to <#{redirect_expected}> but was a redirect to <#{redirect_is}>"
-
assert_operator redirect_expected, :===, redirect_is, message
-
end
-
-
1
private
-
# Proxy to to_param if the object will respond to it.
-
1
def parameterize(value)
-
value.respond_to?(:to_param) ? value.to_param : value
-
end
-
-
1
def normalize_argument_to_redirection(fragment)
-
normalized = case fragment
-
when Regexp
-
fragment
-
when %r{^\w[A-Za-z\d+.-]*:.*}
-
fragment
-
when String
-
@request.protocol + @request.host_with_port + fragment
-
when :back
-
raise RedirectBackError unless refer = @request.headers["Referer"]
-
refer
-
else
-
@controller.url_for(fragment)
-
end
-
-
normalized.respond_to?(:delete) ? normalized.delete("\0\r\n") : normalized
-
end
-
end
-
end
-
end
-
1
require 'uri'
-
1
require 'active_support/core_ext/hash/indifferent_access'
-
1
require 'action_controller/metal/exceptions'
-
-
1
module ActionDispatch
-
1
module Assertions
-
# Suite of assertions to test routes generated by \Rails and the handling of requests made to them.
-
1
module RoutingAssertions
-
# Asserts that the routing of the given +path+ was handled correctly and that the parsed options (given in the +expected_options+ hash)
-
# match +path+. Basically, it asserts that \Rails recognizes the route given by +expected_options+.
-
#
-
# Pass a hash in the second argument (+path+) to specify the request method. This is useful for routes
-
# requiring a specific HTTP method. The hash should contain a :path with the incoming request path
-
# and a :method containing the required HTTP verb.
-
#
-
# # assert that POSTing to /items will call the create action on ItemsController
-
# assert_recognizes({controller: 'items', action: 'create'}, {path: 'items', method: :post})
-
#
-
# You can also pass in +extras+ with a hash containing URL parameters that would normally be in the query string. This can be used
-
# to assert that values in the query string string will end up in the params hash correctly. To test query strings you must use the
-
# extras argument, appending the query string on the path directly will not work. For example:
-
#
-
# # assert that a path of '/items/list/1?view=print' returns the correct options
-
# assert_recognizes({controller: 'items', action: 'list', id: '1', view: 'print'}, 'items/list/1', { view: "print" })
-
#
-
# The +message+ parameter allows you to pass in an error message that is displayed upon failure.
-
#
-
# # Check the default route (i.e., the index action)
-
# assert_recognizes({controller: 'items', action: 'index'}, 'items')
-
#
-
# # Test a specific action
-
# assert_recognizes({controller: 'items', action: 'list'}, 'items/list')
-
#
-
# # Test an action with a parameter
-
# assert_recognizes({controller: 'items', action: 'destroy', id: '1'}, 'items/destroy/1')
-
#
-
# # Test a custom route
-
# assert_recognizes({controller: 'items', action: 'show', id: '1'}, 'view/item1')
-
1
def assert_recognizes(expected_options, path, extras={}, message=nil)
-
request = recognized_request_for(path, extras)
-
-
expected_options = expected_options.clone
-
-
expected_options.stringify_keys!
-
-
message ||= sprintf("The recognized options <%s> did not match <%s>, difference: <%s>",
-
request.path_parameters, expected_options, diff(expected_options, request.path_parameters))
-
assert_equal(expected_options, request.path_parameters, message)
-
end
-
-
# Asserts that the provided options can be used to generate the provided path. This is the inverse of +assert_recognizes+.
-
# The +extras+ parameter is used to tell the request the names and values of additional request parameters that would be in
-
# a query string. The +message+ parameter allows you to specify a custom error message for assertion failures.
-
#
-
# The +defaults+ parameter is unused.
-
#
-
# # Asserts that the default action is generated for a route with no action
-
# assert_generates "/items", controller: "items", action: "index"
-
#
-
# # Tests that the list action is properly routed
-
# assert_generates "/items/list", controller: "items", action: "list"
-
#
-
# # Tests the generation of a route with a parameter
-
# assert_generates "/items/list/1", { controller: "items", action: "list", id: "1" }
-
#
-
# # Asserts that the generated route gives us our custom route
-
# assert_generates "changesets/12", { controller: 'scm', action: 'show_diff', revision: "12" }
-
1
def assert_generates(expected_path, options, defaults={}, extras = {}, message=nil)
-
if expected_path =~ %r{://}
-
fail_on(URI::InvalidURIError) do
-
uri = URI.parse(expected_path)
-
expected_path = uri.path.to_s.empty? ? "/" : uri.path
-
end
-
else
-
expected_path = "/#{expected_path}" unless expected_path.first == '/'
-
end
-
# Load routes.rb if it hasn't been loaded.
-
-
generated_path, extra_keys = @routes.generate_extras(options, defaults)
-
found_extras = options.reject {|k, v| ! extra_keys.include? k}
-
-
msg = message || sprintf("found extras <%s>, not <%s>", found_extras, extras)
-
assert_equal(extras, found_extras, msg)
-
-
msg = message || sprintf("The generated path <%s> did not match <%s>", generated_path,
-
expected_path)
-
assert_equal(expected_path, generated_path, msg)
-
end
-
-
# Asserts that path and options match both ways; in other words, it verifies that <tt>path</tt> generates
-
# <tt>options</tt> and then that <tt>options</tt> generates <tt>path</tt>. This essentially combines +assert_recognizes+
-
# and +assert_generates+ into one step.
-
#
-
# The +extras+ hash allows you to specify options that would normally be provided as a query string to the action. The
-
# +message+ parameter allows you to specify a custom error message to display upon failure.
-
#
-
# # Assert a basic route: a controller with the default action (index)
-
# assert_routing '/home', controller: 'home', action: 'index'
-
#
-
# # Test a route generated with a specific controller, action, and parameter (id)
-
# assert_routing '/entries/show/23', controller: 'entries', action: 'show', id: 23
-
#
-
# # Assert a basic route (controller + default action), with an error message if it fails
-
# assert_routing '/store', { controller: 'store', action: 'index' }, {}, {}, 'Route for store index not generated properly'
-
#
-
# # Tests a route, providing a defaults hash
-
# assert_routing 'controller/action/9', {id: "9", item: "square"}, {controller: "controller", action: "action"}, {}, {item: "square"}
-
#
-
# # Tests a route with a HTTP method
-
# assert_routing({ method: 'put', path: '/product/321' }, { controller: "product", action: "update", id: "321" })
-
1
def assert_routing(path, options, defaults={}, extras={}, message=nil)
-
assert_recognizes(options, path, extras, message)
-
-
controller, default_controller = options[:controller], defaults[:controller]
-
if controller && controller.include?(?/) && default_controller && default_controller.include?(?/)
-
options[:controller] = "/#{controller}"
-
end
-
-
generate_options = options.dup.delete_if{ |k,v| defaults.key?(k) }
-
assert_generates(path.is_a?(Hash) ? path[:path] : path, generate_options, defaults, extras, message)
-
end
-
-
# A helper to make it easier to test different route configurations.
-
# This method temporarily replaces @routes
-
# with a new RouteSet instance.
-
#
-
# The new instance is yielded to the passed block. Typically the block
-
# will create some routes using <tt>set.draw { match ... }</tt>:
-
#
-
# with_routing do |set|
-
# set.draw do
-
# resources :users
-
# end
-
# assert_equal "/users", users_path
-
# end
-
#
-
1
def with_routing
-
old_routes, @routes = @routes, ActionDispatch::Routing::RouteSet.new
-
if defined?(@controller) && @controller
-
old_controller, @controller = @controller, @controller.clone
-
_routes = @routes
-
-
# Unfortunately, there is currently an abstraction leak between AC::Base
-
# and AV::Base which requires having the URL helpers in both AC and AV.
-
# To do this safely at runtime for tests, we need to bump up the helper serial
-
# to that the old AV subclass isn't cached.
-
#
-
# TODO: Make this unnecessary
-
@controller.singleton_class.send(:include, _routes.url_helpers)
-
@controller.view_context_class = Class.new(@controller.view_context_class) do
-
include _routes.url_helpers
-
end
-
end
-
yield @routes
-
ensure
-
@routes = old_routes
-
if defined?(@controller) && @controller
-
@controller = old_controller
-
end
-
end
-
-
# ROUTES TODO: These assertions should really work in an integration context
-
1
def method_missing(selector, *args, &block)
-
if defined?(@controller) && @controller && @routes && @routes.named_routes.helpers.include?(selector)
-
@controller.send(selector, *args, &block)
-
else
-
super
-
end
-
end
-
-
1
private
-
# Recognizes the route for a given path.
-
1
def recognized_request_for(path, extras = {})
-
if path.is_a?(Hash)
-
method = path[:method]
-
path = path[:path]
-
else
-
method = :get
-
end
-
-
# Assume given controller
-
request = ActionController::TestRequest.new
-
-
if path =~ %r{://}
-
fail_on(URI::InvalidURIError) do
-
uri = URI.parse(path)
-
request.env["rack.url_scheme"] = uri.scheme || "http"
-
request.host = uri.host if uri.host
-
request.port = uri.port if uri.port
-
request.path = uri.path.to_s.empty? ? "/" : uri.path
-
end
-
else
-
path = "/#{path}" unless path.first == "/"
-
request.path = path
-
end
-
-
request.request_method = method if method
-
-
params = fail_on(ActionController::RoutingError) do
-
@routes.recognize_path(path, { :method => method, :extras => extras })
-
end
-
request.path_parameters = params.with_indifferent_access
-
-
request
-
end
-
-
1
def fail_on(exception_class)
-
begin
-
yield
-
rescue exception_class => e
-
raise MiniTest::Assertion, e.message
-
end
-
end
-
end
-
end
-
end
-
1
require 'action_view/vendor/html-scanner'
-
1
require 'active_support/core_ext/object/inclusion'
-
-
#--
-
# Copyright (c) 2006 Assaf Arkin (http://labnotes.org)
-
# Under MIT and/or CC By license.
-
#++
-
-
1
module ActionDispatch
-
1
module Assertions
-
1
NO_STRIP = %w{pre script style textarea}
-
-
# Adds the +assert_select+ method for use in Rails functional
-
# test cases, which can be used to make assertions on the response HTML of a controller
-
# action. You can also call +assert_select+ within another +assert_select+ to
-
# make assertions on elements selected by the enclosing assertion.
-
#
-
# Use +css_select+ to select elements without making an assertions, either
-
# from the response HTML or elements selected by the enclosing assertion.
-
#
-
# In addition to HTML responses, you can make the following assertions:
-
#
-
# * +assert_select_encoded+ - Assertions on HTML encoded inside XML, for example for dealing with feed item descriptions.
-
# * +assert_select_email+ - Assertions on the HTML body of an e-mail.
-
#
-
# Also see HTML::Selector to learn how to use selectors.
-
1
module SelectorAssertions
-
# Select and return all matching elements.
-
#
-
# If called with a single argument, uses that argument as a selector
-
# to match all elements of the current page. Returns an empty array
-
# if no match is found.
-
#
-
# If called with two arguments, uses the first argument as the base
-
# element and the second argument as the selector. Attempts to match the
-
# base element and any of its children. Returns an empty array if no
-
# match is found.
-
#
-
# The selector may be a CSS selector expression (String), an expression
-
# with substitution values (Array) or an HTML::Selector object.
-
#
-
# # Selects all div tags
-
# divs = css_select("div")
-
#
-
# # Selects all paragraph tags and does something interesting
-
# pars = css_select("p")
-
# pars.each do |par|
-
# # Do something fun with paragraphs here...
-
# end
-
#
-
# # Selects all list items in unordered lists
-
# items = css_select("ul>li")
-
#
-
# # Selects all form tags and then all inputs inside the form
-
# forms = css_select("form")
-
# forms.each do |form|
-
# inputs = css_select(form, "input")
-
# ...
-
# end
-
1
def css_select(*args)
-
# See assert_select to understand what's going on here.
-
arg = args.shift
-
-
if arg.is_a?(HTML::Node)
-
root = arg
-
arg = args.shift
-
elsif arg == nil
-
raise ArgumentError, "First argument is either selector or element to select, but nil found. Perhaps you called assert_select with an element that does not exist?"
-
elsif defined?(@selected) && @selected
-
matches = []
-
-
@selected.each do |selected|
-
subset = css_select(selected, HTML::Selector.new(arg.dup, args.dup))
-
subset.each do |match|
-
matches << match unless matches.any? { |m| m.equal?(match) }
-
end
-
end
-
-
return matches
-
else
-
root = response_from_page
-
end
-
-
case arg
-
when String
-
selector = HTML::Selector.new(arg, args)
-
when Array
-
selector = HTML::Selector.new(*arg)
-
when HTML::Selector
-
selector = arg
-
else raise ArgumentError, "Expecting a selector as the first argument"
-
end
-
-
selector.select(root)
-
end
-
-
# An assertion that selects elements and makes one or more equality tests.
-
#
-
# If the first argument is an element, selects all matching elements
-
# starting from (and including) that element and all its children in
-
# depth-first order.
-
#
-
# If no element if specified, calling +assert_select+ selects from the
-
# response HTML unless +assert_select+ is called from within an +assert_select+ block.
-
#
-
# When called with a block +assert_select+ passes an array of selected elements
-
# to the block. Calling +assert_select+ from the block, with no element specified,
-
# runs the assertion on the complete set of elements selected by the enclosing assertion.
-
# Alternatively the array may be iterated through so that +assert_select+ can be called
-
# separately for each element.
-
#
-
#
-
# ==== Example
-
# If the response contains two ordered lists, each with four list elements then:
-
# assert_select "ol" do |elements|
-
# elements.each do |element|
-
# assert_select element, "li", 4
-
# end
-
# end
-
#
-
# will pass, as will:
-
# assert_select "ol" do
-
# assert_select "li", 8
-
# end
-
#
-
# The selector may be a CSS selector expression (String), an expression
-
# with substitution values, or an HTML::Selector object.
-
#
-
# === Equality Tests
-
#
-
# The equality test may be one of the following:
-
# * <tt>true</tt> - Assertion is true if at least one element selected.
-
# * <tt>false</tt> - Assertion is true if no element selected.
-
# * <tt>String/Regexp</tt> - Assertion is true if the text value of at least
-
# one element matches the string or regular expression.
-
# * <tt>Integer</tt> - Assertion is true if exactly that number of
-
# elements are selected.
-
# * <tt>Range</tt> - Assertion is true if the number of selected
-
# elements fit the range.
-
# If no equality test specified, the assertion is true if at least one
-
# element selected.
-
#
-
# To perform more than one equality tests, use a hash with the following keys:
-
# * <tt>:text</tt> - Narrow the selection to elements that have this text
-
# value (string or regexp).
-
# * <tt>:html</tt> - Narrow the selection to elements that have this HTML
-
# content (string or regexp).
-
# * <tt>:count</tt> - Assertion is true if the number of selected elements
-
# is equal to this value.
-
# * <tt>:minimum</tt> - Assertion is true if the number of selected
-
# elements is at least this value.
-
# * <tt>:maximum</tt> - Assertion is true if the number of selected
-
# elements is at most this value.
-
#
-
# If the method is called with a block, once all equality tests are
-
# evaluated the block is called with an array of all matched elements.
-
#
-
# ==== Examples
-
#
-
# # At least one form element
-
# assert_select "form"
-
#
-
# # Form element includes four input fields
-
# assert_select "form input", 4
-
#
-
# # Page title is "Welcome"
-
# assert_select "title", "Welcome"
-
#
-
# # Page title is "Welcome" and there is only one title element
-
# assert_select "title", {count: 1, text: "Welcome"},
-
# "Wrong title or more than one title element"
-
#
-
# # Page contains no forms
-
# assert_select "form", false, "This page must contain no forms"
-
#
-
# # Test the content and style
-
# assert_select "body div.header ul.menu"
-
#
-
# # Use substitution values
-
# assert_select "ol>li#?", /item-\d+/
-
#
-
# # All input fields in the form have a name
-
# assert_select "form input" do
-
# assert_select "[name=?]", /.+/ # Not empty
-
# end
-
1
def assert_select(*args, &block)
-
# Start with optional element followed by mandatory selector.
-
arg = args.shift
-
@selected ||= nil
-
-
if arg.is_a?(HTML::Node)
-
# First argument is a node (tag or text, but also HTML root),
-
# so we know what we're selecting from.
-
root = arg
-
arg = args.shift
-
elsif arg == nil
-
# This usually happens when passing a node/element that
-
# happens to be nil.
-
raise ArgumentError, "First argument is either selector or element to select, but nil found. Perhaps you called assert_select with an element that does not exist?"
-
elsif @selected
-
root = HTML::Node.new(nil)
-
root.children.concat @selected
-
else
-
# Otherwise just operate on the response document.
-
root = response_from_page
-
end
-
-
# First or second argument is the selector: string and we pass
-
# all remaining arguments. Array and we pass the argument. Also
-
# accepts selector itself.
-
case arg
-
when String
-
selector = HTML::Selector.new(arg, args)
-
when Array
-
selector = HTML::Selector.new(*arg)
-
when HTML::Selector
-
selector = arg
-
else raise ArgumentError, "Expecting a selector as the first argument"
-
end
-
-
# Next argument is used for equality tests.
-
equals = {}
-
case arg = args.shift
-
when Hash
-
equals = arg
-
when String, Regexp
-
equals[:text] = arg
-
when Integer
-
equals[:count] = arg
-
when Range
-
equals[:minimum] = arg.begin
-
equals[:maximum] = arg.end
-
when FalseClass
-
equals[:count] = 0
-
when NilClass, TrueClass
-
equals[:minimum] = 1
-
else raise ArgumentError, "I don't understand what you're trying to match"
-
end
-
-
# By default we're looking for at least one match.
-
if equals[:count]
-
equals[:minimum] = equals[:maximum] = equals[:count]
-
else
-
equals[:minimum] = 1 unless equals[:minimum]
-
end
-
-
# Last argument is the message we use if the assertion fails.
-
message = args.shift
-
#- message = "No match made with selector #{selector.inspect}" unless message
-
if args.shift
-
raise ArgumentError, "Not expecting that last argument, you either have too many arguments, or they're the wrong type"
-
end
-
-
matches = selector.select(root)
-
# If text/html, narrow down to those elements that match it.
-
content_mismatch = nil
-
if match_with = equals[:text]
-
matches.delete_if do |match|
-
text = ""
-
stack = match.children.reverse
-
while node = stack.pop
-
if node.tag?
-
stack.concat node.children.reverse
-
else
-
content = node.content
-
text << content
-
end
-
end
-
text.strip! unless NO_STRIP.include?(match.name)
-
text.sub!(/\A\n/, '') if match.name == "textarea"
-
unless match_with.is_a?(Regexp) ? (text =~ match_with) : (text == match_with.to_s)
-
content_mismatch ||= sprintf("<%s> expected but was\n<%s>.", match_with, text)
-
true
-
end
-
end
-
elsif match_with = equals[:html]
-
matches.delete_if do |match|
-
html = match.children.map(&:to_s).join
-
html.strip! unless NO_STRIP.include?(match.name)
-
unless match_with.is_a?(Regexp) ? (html =~ match_with) : (html == match_with.to_s)
-
content_mismatch ||= sprintf("<%s> expected but was\n<%s>.", match_with, html)
-
true
-
end
-
end
-
end
-
# Expecting foo found bar element only if found zero, not if
-
# found one but expecting two.
-
message ||= content_mismatch if matches.empty?
-
# Test minimum/maximum occurrence.
-
min, max, count = equals[:minimum], equals[:maximum], equals[:count]
-
-
# FIXME: minitest provides messaging when we use assert_operator,
-
# so is this custom message really needed?
-
message = message || %(Expected #{count_description(min, max, count)} matching "#{selector.to_s}", found #{matches.size}.)
-
if count
-
assert_equal matches.size, count, message
-
else
-
assert_operator matches.size, :>=, min, message if min
-
assert_operator matches.size, :<=, max, message if max
-
end
-
-
# If a block is given call that block. Set @selected to allow
-
# nested assert_select, which can be nested several levels deep.
-
if block_given? && !matches.empty?
-
begin
-
in_scope, @selected = @selected, matches
-
yield matches
-
ensure
-
@selected = in_scope
-
end
-
end
-
-
# Returns all matches elements.
-
matches
-
end
-
-
1
def count_description(min, max, count) #:nodoc:
-
pluralize = lambda {|word, quantity| word << (quantity == 1 ? '' : 's')}
-
-
if min && max && (max != min)
-
"between #{min} and #{max} elements"
-
elsif min && max && max == min && count
-
"exactly #{count} #{pluralize['element', min]}"
-
elsif min && !(min == 1 && max == 1)
-
"at least #{min} #{pluralize['element', min]}"
-
elsif max
-
"at most #{max} #{pluralize['element', max]}"
-
end
-
end
-
-
# Extracts the content of an element, treats it as encoded HTML and runs
-
# nested assertion on it.
-
#
-
# You typically call this method within another assertion to operate on
-
# all currently selected elements. You can also pass an element or array
-
# of elements.
-
#
-
# The content of each element is un-encoded, and wrapped in the root
-
# element +encoded+. It then calls the block with all un-encoded elements.
-
#
-
# # Selects all bold tags from within the title of an Atom feed's entries (perhaps to nab a section name prefix)
-
# assert_select "feed[xmlns='http://www.w3.org/2005/Atom']" do
-
# # Select each entry item and then the title item
-
# assert_select "entry>title" do
-
# # Run assertions on the encoded title elements
-
# assert_select_encoded do
-
# assert_select "b"
-
# end
-
# end
-
# end
-
#
-
#
-
# # Selects all paragraph tags from within the description of an RSS feed
-
# assert_select "rss[version=2.0]" do
-
# # Select description element of each feed item.
-
# assert_select "channel>item>description" do
-
# # Run assertions on the encoded elements.
-
# assert_select_encoded do
-
# assert_select "p"
-
# end
-
# end
-
# end
-
1
def assert_select_encoded(element = nil, &block)
-
case element
-
when Array
-
elements = element
-
when HTML::Node
-
elements = [element]
-
when nil
-
unless elements = @selected
-
raise ArgumentError, "First argument is optional, but must be called from a nested assert_select"
-
end
-
else
-
raise ArgumentError, "Argument is optional, and may be node or array of nodes"
-
end
-
-
fix_content = lambda do |node|
-
# Gets around a bug in the Rails 1.1 HTML parser.
-
node.content.gsub(/<!\[CDATA\[(.*)(\]\]>)?/m) { Rack::Utils.escapeHTML($1) }
-
end
-
-
selected = elements.map do |_element|
-
text = _element.children.select{ |c| not c.tag? }.map{ |c| fix_content[c] }.join
-
root = HTML::Document.new(CGI.unescapeHTML("<encoded>#{text}</encoded>")).root
-
css_select(root, "encoded:root", &block)[0]
-
end
-
-
begin
-
old_selected, @selected = @selected, selected
-
assert_select ":root", &block
-
ensure
-
@selected = old_selected
-
end
-
end
-
-
# Extracts the body of an email and runs nested assertions on it.
-
#
-
# You must enable deliveries for this assertion to work, use:
-
# ActionMailer::Base.perform_deliveries = true
-
#
-
# assert_select_email do
-
# assert_select "h1", "Email alert"
-
# end
-
#
-
# assert_select_email do
-
# items = assert_select "ol>li"
-
# items.each do
-
# # Work with items here...
-
# end
-
# end
-
1
def assert_select_email(&block)
-
deliveries = ActionMailer::Base.deliveries
-
assert !deliveries.empty?, "No e-mail in delivery list"
-
-
deliveries.each do |delivery|
-
(delivery.parts.empty? ? [delivery] : delivery.parts).each do |part|
-
if part["Content-Type"].to_s =~ /^text\/html\W/
-
root = HTML::Document.new(part.body.to_s).root
-
assert_select root, ":root", &block
-
end
-
end
-
end
-
end
-
-
1
protected
-
# +assert_select+ and +css_select+ call this to obtain the content in the HTML page.
-
1
def response_from_page
-
html_document.root
-
end
-
end
-
end
-
end
-
1
require 'action_view/vendor/html-scanner'
-
-
1
module ActionDispatch
-
1
module Assertions
-
# Pair of assertions to testing elements in the HTML output of the response.
-
1
module TagAssertions
-
# Asserts that there is a tag/node/element in the body of the response
-
# that meets all of the given conditions. The +conditions+ parameter must
-
# be a hash of any of the following keys (all are optional):
-
#
-
# * <tt>:tag</tt>: the node type must match the corresponding value
-
# * <tt>:attributes</tt>: a hash. The node's attributes must match the
-
# corresponding values in the hash.
-
# * <tt>:parent</tt>: a hash. The node's parent must match the
-
# corresponding hash.
-
# * <tt>:child</tt>: a hash. At least one of the node's immediate children
-
# must meet the criteria described by the hash.
-
# * <tt>:ancestor</tt>: a hash. At least one of the node's ancestors must
-
# meet the criteria described by the hash.
-
# * <tt>:descendant</tt>: a hash. At least one of the node's descendants
-
# must meet the criteria described by the hash.
-
# * <tt>:sibling</tt>: a hash. At least one of the node's siblings must
-
# meet the criteria described by the hash.
-
# * <tt>:after</tt>: a hash. The node must be after any sibling meeting
-
# the criteria described by the hash, and at least one sibling must match.
-
# * <tt>:before</tt>: a hash. The node must be before any sibling meeting
-
# the criteria described by the hash, and at least one sibling must match.
-
# * <tt>:children</tt>: a hash, for counting children of a node. Accepts
-
# the keys:
-
# * <tt>:count</tt>: either a number or a range which must equal (or
-
# include) the number of children that match.
-
# * <tt>:less_than</tt>: the number of matching children must be less
-
# than this number.
-
# * <tt>:greater_than</tt>: the number of matching children must be
-
# greater than this number.
-
# * <tt>:only</tt>: another hash consisting of the keys to use
-
# to match on the children, and only matching children will be
-
# counted.
-
# * <tt>:content</tt>: the textual content of the node must match the
-
# given value. This will not match HTML tags in the body of a
-
# tag--only text.
-
#
-
# Conditions are matched using the following algorithm:
-
#
-
# * if the condition is a string, it must be a substring of the value.
-
# * if the condition is a regexp, it must match the value.
-
# * if the condition is a number, the value must match number.to_s.
-
# * if the condition is +true+, the value must not be +nil+.
-
# * if the condition is +false+ or +nil+, the value must be +nil+.
-
#
-
# # Assert that there is a "span" tag
-
# assert_tag tag: "span"
-
#
-
# # Assert that there is a "span" tag with id="x"
-
# assert_tag tag: "span", attributes: { id: "x" }
-
#
-
# # Assert that there is a "span" tag using the short-hand
-
# assert_tag :span
-
#
-
# # Assert that there is a "span" tag with id="x" using the short-hand
-
# assert_tag :span, attributes: { id: "x" }
-
#
-
# # Assert that there is a "span" inside of a "div"
-
# assert_tag tag: "span", parent: { tag: "div" }
-
#
-
# # Assert that there is a "span" somewhere inside a table
-
# assert_tag tag: "span", ancestor: { tag: "table" }
-
#
-
# # Assert that there is a "span" with at least one "em" child
-
# assert_tag tag: "span", child: { tag: "em" }
-
#
-
# # Assert that there is a "span" containing a (possibly nested)
-
# # "strong" tag.
-
# assert_tag tag: "span", descendant: { tag: "strong" }
-
#
-
# # Assert that there is a "span" containing between 2 and 4 "em" tags
-
# # as immediate children
-
# assert_tag tag: "span",
-
# children: { count: 2..4, only: { tag: "em" } }
-
#
-
# # Get funky: assert that there is a "div", with an "ul" ancestor
-
# # and an "li" parent (with "class" = "enum"), and containing a
-
# # "span" descendant that contains text matching /hello world/
-
# assert_tag tag: "div",
-
# ancestor: { tag: "ul" },
-
# parent: { tag: "li",
-
# attributes: { class: "enum" } },
-
# descendant: { tag: "span",
-
# child: /hello world/ }
-
#
-
# <b>Please note</b>: +assert_tag+ and +assert_no_tag+ only work
-
# with well-formed XHTML. They recognize a few tags as implicitly self-closing
-
# (like br and hr and such) but will not work correctly with tags
-
# that allow optional closing tags (p, li, td). <em>You must explicitly
-
# close all of your tags to use these assertions.</em>
-
1
def assert_tag(*opts)
-
opts = opts.size > 1 ? opts.last.merge({ :tag => opts.first.to_s }) : opts.first
-
tag = find_tag(opts)
-
assert tag, "expected tag, but no tag found matching #{opts.inspect} in:\n#{@response.body.inspect}"
-
end
-
-
# Identical to +assert_tag+, but asserts that a matching tag does _not_
-
# exist. (See +assert_tag+ for a full discussion of the syntax.)
-
#
-
# # Assert that there is not a "div" containing a "p"
-
# assert_no_tag tag: "div", descendant: { tag: "p" }
-
#
-
# # Assert that an unordered list is empty
-
# assert_no_tag tag: "ul", descendant: { tag: "li" }
-
#
-
# # Assert that there is not a "p" tag with between 1 to 3 "img" tags
-
# # as immediate children
-
# assert_no_tag tag: "p",
-
# children: { count: 1..3, only: { tag: "img" } }
-
1
def assert_no_tag(*opts)
-
opts = opts.size > 1 ? opts.last.merge({ :tag => opts.first.to_s }) : opts.first
-
tag = find_tag(opts)
-
assert !tag, "expected no tag, but found tag matching #{opts.inspect} in:\n#{@response.body.inspect}"
-
end
-
-
1
def find_tag(conditions)
-
html_document.find(conditions)
-
end
-
-
1
def find_all_tag(conditions)
-
html_document.find_all(conditions)
-
end
-
-
1
def html_document
-
xml = @response.content_type =~ /xml$/
-
@html_document ||= HTML::Document.new(@response.body, false, xml)
-
end
-
end
-
end
-
end
-
1
require 'stringio'
-
1
require 'uri'
-
1
require 'active_support/core_ext/kernel/singleton_class'
-
1
require 'active_support/core_ext/object/try'
-
1
require 'rack/test'
-
1
require 'minitest/unit'
-
-
1
module ActionDispatch
-
1
module Integration #:nodoc:
-
1
module RequestHelpers
-
# Performs a GET request with the given parameters.
-
#
-
# - +path+: The URI (as a String) on which you want to perform a GET
-
# request.
-
# - +parameters+: The HTTP parameters that you want to pass. This may
-
# be +nil+,
-
# a Hash, or a String that is appropriately encoded
-
# (<tt>application/x-www-form-urlencoded</tt> or
-
# <tt>multipart/form-data</tt>).
-
# - +headers+: Additional headers to pass, as a Hash. The headers will be
-
# merged into the Rack env hash.
-
#
-
# This method returns a Response object, which one can use to
-
# inspect the details of the response. Furthermore, if this method was
-
# called from an ActionDispatch::IntegrationTest object, then that
-
# object's <tt>@response</tt> instance variable will point to the same
-
# response object.
-
#
-
# You can also perform POST, PATCH, PUT, DELETE, and HEAD requests with
-
# +#post+, +#patch+, +#put+, +#delete+, and +#head+.
-
1
def get(path, parameters = nil, headers = nil)
-
1
process :get, path, parameters, headers
-
end
-
-
# Performs a POST request with the given parameters. See +#get+ for more
-
# details.
-
1
def post(path, parameters = nil, headers = nil)
-
process :post, path, parameters, headers
-
end
-
-
# Performs a PATCH request with the given parameters. See +#get+ for more
-
# details.
-
1
def patch(path, parameters = nil, headers = nil)
-
process :patch, path, parameters, headers
-
end
-
-
# Performs a PUT request with the given parameters. See +#get+ for more
-
# details.
-
1
def put(path, parameters = nil, headers = nil)
-
process :put, path, parameters, headers
-
end
-
-
# Performs a DELETE request with the given parameters. See +#get+ for
-
# more details.
-
1
def delete(path, parameters = nil, headers = nil)
-
process :delete, path, parameters, headers
-
end
-
-
# Performs a HEAD request with the given parameters. See +#get+ for more
-
# details.
-
1
def head(path, parameters = nil, headers = nil)
-
process :head, path, parameters, headers
-
end
-
-
# Performs a OPTIONS request with the given parameters. See +#get+ for
-
# more details.
-
1
def options(path, parameters = nil, headers = nil)
-
process :options, path, parameters, headers
-
end
-
-
# Performs an XMLHttpRequest request with the given parameters, mirroring
-
# a request from the Prototype library.
-
#
-
# The request_method is +:get+, +:post+, +:patch+, +:put+, +:delete+ or
-
# +:head+; the parameters are +nil+, a hash, or a url-encoded or multipart
-
# string; the headers are a hash.
-
1
def xml_http_request(request_method, path, parameters = nil, headers = nil)
-
headers ||= {}
-
headers['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest'
-
headers['HTTP_ACCEPT'] ||= [Mime::JS, Mime::HTML, Mime::XML, 'text/xml', Mime::ALL].join(', ')
-
process(request_method, path, parameters, headers)
-
end
-
1
alias xhr :xml_http_request
-
-
# Follow a single redirect response. If the last response was not a
-
# redirect, an exception will be raised. Otherwise, the redirect is
-
# performed on the location header.
-
1
def follow_redirect!
-
raise "not a redirect! #{status} #{status_message}" unless redirect?
-
get(response.location)
-
status
-
end
-
-
# Performs a request using the specified method, following any subsequent
-
# redirect. Note that the redirects are followed until the response is
-
# not a redirect--this means you may run into an infinite loop if your
-
# redirect loops back to itself.
-
1
def request_via_redirect(http_method, path, parameters = nil, headers = nil)
-
process(http_method, path, parameters, headers)
-
follow_redirect! while redirect?
-
status
-
end
-
-
# Performs a GET request, following any subsequent redirect.
-
# See +request_via_redirect+ for more information.
-
1
def get_via_redirect(path, parameters = nil, headers = nil)
-
request_via_redirect(:get, path, parameters, headers)
-
end
-
-
# Performs a POST request, following any subsequent redirect.
-
# See +request_via_redirect+ for more information.
-
1
def post_via_redirect(path, parameters = nil, headers = nil)
-
request_via_redirect(:post, path, parameters, headers)
-
end
-
-
# Performs a PATCH request, following any subsequent redirect.
-
# See +request_via_redirect+ for more information.
-
1
def patch_via_redirect(path, parameters = nil, headers = nil)
-
request_via_redirect(:patch, path, parameters, headers)
-
end
-
-
# Performs a PUT request, following any subsequent redirect.
-
# See +request_via_redirect+ for more information.
-
1
def put_via_redirect(path, parameters = nil, headers = nil)
-
request_via_redirect(:put, path, parameters, headers)
-
end
-
-
# Performs a DELETE request, following any subsequent redirect.
-
# See +request_via_redirect+ for more information.
-
1
def delete_via_redirect(path, parameters = nil, headers = nil)
-
request_via_redirect(:delete, path, parameters, headers)
-
end
-
end
-
-
# An instance of this class represents a set of requests and responses
-
# performed sequentially by a test process. Because you can instantiate
-
# multiple sessions and run them side-by-side, you can also mimic (to some
-
# limited extent) multiple simultaneous users interacting with your system.
-
#
-
# Typically, you will instantiate a new session using
-
# IntegrationTest#open_session, rather than instantiating
-
# Integration::Session directly.
-
1
class Session
-
1
DEFAULT_HOST = "www.example.com"
-
-
1
include MiniTest::Assertions
-
1
include TestProcess, RequestHelpers, Assertions
-
-
1
%w( status status_message headers body redirect? ).each do |method|
-
5
delegate method, :to => :response, :allow_nil => true
-
end
-
-
1
%w( path ).each do |method|
-
1
delegate method, :to => :request, :allow_nil => true
-
end
-
-
# The hostname used in the last request.
-
1
def host
-
3
@host || DEFAULT_HOST
-
end
-
1
attr_writer :host
-
-
# The remote_addr used in the last request.
-
1
attr_accessor :remote_addr
-
-
# The Accept header to send.
-
1
attr_accessor :accept
-
-
# A map of the cookies returned by the last response, and which will be
-
# sent with the next request.
-
1
def cookies
-
_mock_session.cookie_jar
-
end
-
-
# A reference to the controller instance used by the last request.
-
1
attr_reader :controller
-
-
# A reference to the request instance used by the last request.
-
1
attr_reader :request
-
-
# A reference to the response instance used by the last request.
-
1
attr_reader :response
-
-
# A running counter of the number of requests processed.
-
1
attr_accessor :request_count
-
-
1
include ActionDispatch::Routing::UrlFor
-
-
# Create and initialize a new Session instance.
-
1
def initialize(app)
-
1
super()
-
1
@app = app
-
-
# If the app is a Rails app, make url_helpers available on the session
-
# This makes app.url_for and app.foo_path available in the console
-
1
if app.respond_to?(:routes)
-
1
singleton_class.class_eval do
-
1
include app.routes.url_helpers if app.routes.respond_to?(:url_helpers)
-
1
include app.routes.mounted_helpers if app.routes.respond_to?(:mounted_helpers)
-
end
-
end
-
-
1
reset!
-
end
-
-
1
def url_options
-
@url_options ||= default_url_options.dup.tap do |url_options|
-
url_options.reverse_merge!(controller.url_options) if controller
-
-
if @app.respond_to?(:routes) && @app.routes.respond_to?(:default_url_options)
-
url_options.reverse_merge!(@app.routes.default_url_options)
-
end
-
-
url_options.reverse_merge!(:host => host, :protocol => https? ? "https" : "http")
-
end
-
end
-
-
# Resets the instance. This can be used to reset the state information
-
# in an existing session instance, so it can be used from a clean-slate
-
# condition.
-
#
-
# session.reset!
-
1
def reset!
-
1
@https = false
-
1
@controller = @request = @response = nil
-
1
@_mock_session = nil
-
1
@request_count = 0
-
1
@url_options = nil
-
-
1
self.host = DEFAULT_HOST
-
1
self.remote_addr = "127.0.0.1"
-
1
self.accept = "text/xml,application/xml,application/xhtml+xml," +
-
"text/html;q=0.9,text/plain;q=0.8,image/png," +
-
"*/*;q=0.5"
-
-
1
unless defined? @named_routes_configured
-
# the helpers are made protected by default--we make them public for
-
# easier access during testing and troubleshooting.
-
1
@named_routes_configured = true
-
end
-
end
-
-
# Specify whether or not the session should mimic a secure HTTPS request.
-
#
-
# session.https!
-
# session.https!(false)
-
1
def https!(flag = true)
-
@https = flag
-
end
-
-
# Return +true+ if the session is mimicking a secure HTTPS request.
-
#
-
# if session.https?
-
# ...
-
# end
-
1
def https?
-
3
@https
-
end
-
-
# Set the host name to use in the next request.
-
#
-
# session.host! "www.example.com"
-
1
alias :host! :host=
-
-
1
private
-
1
def _mock_session
-
2
@_mock_session ||= Rack::MockSession.new(@app, host)
-
end
-
-
# Performs the actual request.
-
1
def process(method, path, parameters = nil, rack_env = nil)
-
1
rack_env ||= {}
-
1
if path =~ %r{://}
-
location = URI.parse(path)
-
https! URI::HTTPS === location if location.scheme
-
host! location.host if location.host
-
path = location.query ? "#{location.path}?#{location.query}" : location.path
-
end
-
-
1
unless ActionController::Base < ActionController::Testing
-
1
ActionController::Base.class_eval do
-
1
include ActionController::Testing
-
end
-
end
-
-
1
hostname, port = host.split(':')
-
-
1
env = {
-
:method => method,
-
:params => parameters,
-
-
"SERVER_NAME" => hostname,
-
1
"SERVER_PORT" => port || (https? ? "443" : "80"),
-
"HTTPS" => https? ? "on" : "off",
-
"rack.url_scheme" => https? ? "https" : "http",
-
-
"REQUEST_URI" => path,
-
"HTTP_HOST" => host,
-
"REMOTE_ADDR" => remote_addr,
-
"CONTENT_TYPE" => "application/x-www-form-urlencoded",
-
"HTTP_ACCEPT" => accept
-
}
-
-
1
session = Rack::Test::Session.new(_mock_session)
-
-
1
env.merge!(rack_env)
-
-
# NOTE: rack-test v0.5 doesn't build a default uri correctly
-
# Make sure requested path is always a full uri
-
1
uri = URI.parse('/')
-
1
uri.scheme ||= env['rack.url_scheme']
-
1
uri.host ||= env['SERVER_NAME']
-
1
uri.port ||= env['SERVER_PORT'].try(:to_i)
-
1
uri += path
-
-
1
session.request(uri.to_s, env)
-
-
1
@request_count += 1
-
1
@request = ActionDispatch::Request.new(session.last_request.env)
-
1
response = _mock_session.last_response
-
1
@response = ActionDispatch::TestResponse.new(response.status, response.headers, response.body)
-
1
@html_document = nil
-
1
@url_options = nil
-
-
1
@controller = session.last_request.env['action_controller.instance']
-
-
1
return response.status
-
end
-
end
-
-
1
module Runner
-
1
include ActionDispatch::Assertions
-
-
1
def app
-
@app ||= nil
-
end
-
-
# Reset the current session. This is useful for testing multiple sessions
-
# in a single test case.
-
1
def reset!
-
1
@integration_session = Integration::Session.new(app)
-
end
-
-
1
%w(get post patch put head delete options cookies assigns
-
xml_http_request xhr get_via_redirect post_via_redirect).each do |method|
-
13
define_method(method) do |*args|
-
1
reset! unless integration_session
-
# reset the html_document variable, but only for new get/post calls
-
1
@html_document = nil unless method == 'cookies' || method == 'assigns'
-
1
integration_session.__send__(method, *args).tap do
-
1
copy_session_variables!
-
end
-
end
-
end
-
-
# Open a new session instance. If a block is given, the new session is
-
# yielded to the block before being returned.
-
#
-
# session = open_session do |sess|
-
# sess.extend(CustomAssertions)
-
# end
-
#
-
# By default, a single session is automatically created for you, but you
-
# can use this method to open multiple sessions that ought to be tested
-
# simultaneously.
-
1
def open_session(app = nil)
-
dup.tap do |session|
-
yield session if block_given?
-
end
-
end
-
-
# Copy the instance variables from the current session instance into the
-
# test instance.
-
1
def copy_session_variables! #:nodoc:
-
1
return unless integration_session
-
1
%w(controller response request).each do |var|
-
3
instance_variable_set("@#{var}", @integration_session.__send__(var))
-
end
-
end
-
-
1
def default_url_options
-
reset! unless integration_session
-
integration_session.default_url_options
-
end
-
-
1
def default_url_options=(options)
-
reset! unless integration_session
-
integration_session.default_url_options = options
-
end
-
-
1
def respond_to?(method, include_private = false)
-
2
integration_session.respond_to?(method, include_private) || super
-
end
-
-
# Delegate unhandled messages to the current session instance.
-
1
def method_missing(sym, *args, &block)
-
reset! unless integration_session
-
if integration_session.respond_to?(sym)
-
integration_session.__send__(sym, *args, &block).tap do
-
copy_session_variables!
-
end
-
else
-
super
-
end
-
end
-
-
1
private
-
1
def integration_session
-
5
@integration_session ||= nil
-
end
-
end
-
end
-
-
# An integration test spans multiple controllers and actions,
-
# tying them all together to ensure they work together as expected. It tests
-
# more completely than either unit or functional tests do, exercising the
-
# entire stack, from the dispatcher to the database.
-
#
-
# At its simplest, you simply extend <tt>IntegrationTest</tt> and write your tests
-
# using the get/post methods:
-
#
-
# require "test_helper"
-
#
-
# class ExampleTest < ActionDispatch::IntegrationTest
-
# fixtures :people
-
#
-
# def test_login
-
# # get the login page
-
# get "/login"
-
# assert_equal 200, status
-
#
-
# # post the login and follow through to the home page
-
# post "/login", username: people(:jamis).username,
-
# password: people(:jamis).password
-
# follow_redirect!
-
# assert_equal 200, status
-
# assert_equal "/home", path
-
# end
-
# end
-
#
-
# However, you can also have multiple session instances open per test, and
-
# even extend those instances with assertions and methods to create a very
-
# powerful testing DSL that is specific for your application. You can even
-
# reference any named routes you happen to have defined.
-
#
-
# require "test_helper"
-
#
-
# class AdvancedTest < ActionDispatch::IntegrationTest
-
# fixtures :people, :rooms
-
#
-
# def test_login_and_speak
-
# jamis, david = login(:jamis), login(:david)
-
# room = rooms(:office)
-
#
-
# jamis.enter(room)
-
# jamis.speak(room, "anybody home?")
-
#
-
# david.enter(room)
-
# david.speak(room, "hello!")
-
# end
-
#
-
# private
-
#
-
# module CustomAssertions
-
# def enter(room)
-
# # reference a named route, for maximum internal consistency!
-
# get(room_url(id: room.id))
-
# assert(...)
-
# ...
-
# end
-
#
-
# def speak(room, message)
-
# xml_http_request "/say/#{room.id}", message: message
-
# assert(...)
-
# ...
-
# end
-
# end
-
#
-
# def login(who)
-
# open_session do |sess|
-
# sess.extend(CustomAssertions)
-
# who = people(who)
-
# sess.post "/login", username: who.username,
-
# password: who.password
-
# assert(...)
-
# end
-
# end
-
# end
-
1
class IntegrationTest < ActiveSupport::TestCase
-
1
include Integration::Runner
-
1
include ActionController::TemplateAssertions
-
1
include ActionDispatch::Routing::UrlFor
-
-
# Use AD::IntegrationTest for acceptance tests
-
1
register_spec_type(/(Acceptance|Integration) ?Test\z/i, self)
-
-
1
@@app = nil
-
-
1
def self.app
-
if !@@app && !ActionDispatch.test_app
-
ActiveSupport::Deprecation.warn "Rails application fallback is deprecated and no longer works, please set ActionDispatch.test_app"
-
end
-
-
@@app || ActionDispatch.test_app
-
end
-
-
1
def self.app=(app)
-
@@app = app
-
end
-
-
1
def app
-
super || self.class.app
-
end
-
-
1
def url_options
-
reset! unless integration_session
-
integration_session.url_options
-
end
-
end
-
end
-
1
require 'action_dispatch/middleware/cookies'
-
1
require 'action_dispatch/middleware/flash'
-
1
require 'active_support/core_ext/hash/indifferent_access'
-
-
1
module ActionDispatch
-
1
module TestProcess
-
1
def assigns(key = nil)
-
assigns = {}.with_indifferent_access
-
@controller.view_assigns.each {|k, v| assigns.regular_writer(k, v)}
-
key.nil? ? assigns : assigns[key]
-
end
-
-
1
def session
-
@request.session
-
end
-
-
1
def flash
-
@request.flash
-
end
-
-
1
def cookies
-
@request.cookie_jar
-
end
-
-
1
def redirect_to_url
-
@response.redirect_url
-
end
-
-
# Shortcut for <tt>Rack::Test::UploadedFile.new(ActionController::TestCase.fixture_path + path, type)</tt>:
-
#
-
# post :change_avatar, avatar: fixture_file_upload('/files/spongebob.png', 'image/png')
-
#
-
# To upload binary files on Windows, pass <tt>:binary</tt> as the last parameter.
-
# This will not affect other platforms:
-
#
-
# post :change_avatar, avatar: fixture_file_upload('/files/spongebob.png', 'image/png', :binary)
-
1
def fixture_file_upload(path, mime_type = nil, binary = false)
-
fixture_path = self.class.fixture_path if self.class.respond_to?(:fixture_path)
-
Rack::Test::UploadedFile.new("#{fixture_path}#{path}", mime_type, binary)
-
end
-
end
-
end
-
1
require 'active_support/core_ext/hash/indifferent_access'
-
1
require 'rack/utils'
-
-
1
module ActionDispatch
-
1
class TestRequest < Request
-
1
DEFAULT_ENV = Rack::MockRequest.env_for('/')
-
-
1
def self.new(env = {})
-
super
-
end
-
-
1
def initialize(env = {})
-
env = Rails.application.env_config.merge(env) if defined?(Rails.application) && Rails.application
-
super(default_env.merge(env))
-
-
self.host = 'test.host'
-
self.remote_addr = '0.0.0.0'
-
self.user_agent = 'Rails Testing'
-
end
-
-
1
def request_method=(method)
-
@env['REQUEST_METHOD'] = method.to_s.upcase
-
end
-
-
1
def host=(host)
-
@env['HTTP_HOST'] = host
-
end
-
-
1
def port=(number)
-
@env['SERVER_PORT'] = number.to_i
-
end
-
-
1
def request_uri=(uri)
-
@env['REQUEST_URI'] = uri
-
end
-
-
1
def path=(path)
-
@env['PATH_INFO'] = path
-
end
-
-
1
def action=(action_name)
-
path_parameters["action"] = action_name.to_s
-
end
-
-
1
def if_modified_since=(last_modified)
-
@env['HTTP_IF_MODIFIED_SINCE'] = last_modified
-
end
-
-
1
def if_none_match=(etag)
-
@env['HTTP_IF_NONE_MATCH'] = etag
-
end
-
-
1
def remote_addr=(addr)
-
@env['REMOTE_ADDR'] = addr
-
end
-
-
1
def user_agent=(user_agent)
-
@env['HTTP_USER_AGENT'] = user_agent
-
end
-
-
1
def accept=(mime_types)
-
@env.delete('action_dispatch.request.accepts')
-
@env['HTTP_ACCEPT'] = Array(mime_types).collect { |mime_type| mime_type.to_s }.join(",")
-
end
-
-
1
alias :rack_cookies :cookies
-
-
1
def cookies
-
@cookies ||= {}.with_indifferent_access
-
end
-
-
1
private
-
-
1
def default_env
-
DEFAULT_ENV
-
end
-
end
-
end
-
1
module ActionDispatch
-
# Integration test methods such as ActionDispatch::Integration::Session#get
-
# and ActionDispatch::Integration::Session#post return objects of class
-
# TestResponse, which represent the HTTP response results of the requested
-
# controller actions.
-
#
-
# See Response for more information on controller response objects.
-
1
class TestResponse < Response
-
1
def self.from_response(response)
-
new.tap do |resp|
-
resp.status = response.status
-
resp.headers = response.headers
-
resp.body = response.body
-
end
-
end
-
-
# Was the response successful?
-
1
alias_method :success?, :successful?
-
-
# Was the URL not found?
-
1
alias_method :missing?, :not_found?
-
-
# Were we redirected?
-
1
alias_method :redirect?, :redirection?
-
-
# Was there a server-side error?
-
1
alias_method :error?, :server_error?
-
end
-
end
-
#--
-
# Copyright (c) 2004-2012 David Heinemeier Hansson
-
#
-
# Permission is hereby granted, free of charge, to any person obtaining
-
# a copy of this software and associated documentation files (the
-
# "Software"), to deal in the Software without restriction, including
-
# without limitation the rights to use, copy, modify, merge, publish,
-
# distribute, sublicense, and/or sell copies of the Software, and to
-
# permit persons to whom the Software is furnished to do so, subject to
-
# the following conditions:
-
#
-
# The above copyright notice and this permission notice shall be
-
# included in all copies or substantial portions of the Software.
-
#
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
#++
-
-
1
require 'action_pack/version'
-
1
module ActionPack
-
1
module VERSION #:nodoc:
-
1
MAJOR = 4
-
1
MINOR = 0
-
1
TINY = 0
-
1
PRE = "beta"
-
-
1
STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
-
end
-
end
-
#--
-
# Copyright (c) 2004-2012 David Heinemeier Hansson
-
#
-
# Permission is hereby granted, free of charge, to any person obtaining
-
# a copy of this software and associated documentation files (the
-
# "Software"), to deal in the Software without restriction, including
-
# without limitation the rights to use, copy, modify, merge, publish,
-
# distribute, sublicense, and/or sell copies of the Software, and to
-
# permit persons to whom the Software is furnished to do so, subject to
-
# the following conditions:
-
#
-
# The above copyright notice and this permission notice shall be
-
# included in all copies or substantial portions of the Software.
-
#
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
#++
-
-
1
require 'active_support'
-
1
require 'active_support/rails'
-
1
require 'action_pack'
-
-
1
module ActionView
-
1
extend ActiveSupport::Autoload
-
-
1
eager_autoload do
-
1
autoload :Base
-
1
autoload :Context
-
1
autoload :CompiledTemplates, "action_view/context"
-
1
autoload :Digestor
-
1
autoload :Helpers
-
1
autoload :LookupContext
-
1
autoload :PathSet
-
1
autoload :RecordIdentifier
-
1
autoload :RoutingUrlFor
-
1
autoload :Template
-
-
1
autoload_under "renderer" do
-
1
autoload :Renderer
-
1
autoload :AbstractRenderer
-
1
autoload :PartialRenderer
-
1
autoload :TemplateRenderer
-
1
autoload :StreamingTemplateRenderer
-
end
-
-
1
autoload_at "action_view/template/resolver" do
-
1
autoload :Resolver
-
1
autoload :PathResolver
-
1
autoload :FileSystemResolver
-
1
autoload :OptimizedFileSystemResolver
-
1
autoload :FallbackFileSystemResolver
-
end
-
-
1
autoload_at "action_view/buffers" do
-
1
autoload :OutputBuffer
-
1
autoload :StreamingBuffer
-
end
-
-
1
autoload_at "action_view/flows" do
-
1
autoload :OutputFlow
-
1
autoload :StreamingFlow
-
end
-
-
1
autoload_at "action_view/template/error" do
-
1
autoload :MissingTemplate
-
1
autoload :ActionViewError
-
1
autoload :EncodingError
-
1
autoload :MissingRequestError
-
1
autoload :TemplateError
-
1
autoload :WrongEncodingError
-
end
-
end
-
-
1
autoload :TestCase
-
-
1
ENCODING_FLAG = '#.*coding[:=]\s*(\S+)[ \t]*'
-
-
1
def self.eager_load!
-
super
-
ActionView::Template.eager_load!
-
end
-
end
-
-
1
require 'active_support/core_ext/string/output_safety'
-
-
1
ActiveSupport.on_load(:i18n) do
-
1
I18n.load_path << "#{File.dirname(__FILE__)}/action_view/locale/en.yml"
-
end
-
1
require 'active_support/core_ext/module/attr_internal'
-
1
require 'active_support/core_ext/class/attribute_accessors'
-
1
require 'active_support/ordered_options'
-
1
require 'action_view/log_subscriber'
-
-
1
module ActionView #:nodoc:
-
# = Action View Base
-
#
-
# Action View templates can be written in several ways. If the template file has a <tt>.erb</tt> extension then it uses a mixture of ERb
-
# (included in Ruby) and HTML. If the template file has a <tt>.builder</tt> extension then Jim Weirich's Builder::XmlMarkup library is used.
-
#
-
# == ERB
-
#
-
# You trigger ERB by using embeddings such as <% %>, <% -%>, and <%= %>. The <%= %> tag set is used when you want output. Consider the
-
# following loop for names:
-
#
-
# <b>Names of all the people</b>
-
# <% @people.each do |person| %>
-
# Name: <%= person.name %><br/>
-
# <% end %>
-
#
-
# The loop is setup in regular embedding tags <% %> and the name is written using the output embedding tag <%= %>. Note that this
-
# is not just a usage suggestion. Regular output functions like print or puts won't work with ERB templates. So this would be wrong:
-
#
-
# <%# WRONG %>
-
# Hi, Mr. <% puts "Frodo" %>
-
#
-
# If you absolutely must write from within a function use +concat+.
-
#
-
# <%- and -%> suppress leading and trailing whitespace, including the trailing newline, and can be used interchangeably with <% and %>.
-
#
-
# === Using sub templates
-
#
-
# Using sub templates allows you to sidestep tedious replication and extract common display structures in shared templates. The
-
# classic example is the use of a header and footer (even though the Action Pack-way would be to use Layouts):
-
#
-
# <%= render "shared/header" %>
-
# Something really specific and terrific
-
# <%= render "shared/footer" %>
-
#
-
# As you see, we use the output embeddings for the render methods. The render call itself will just return a string holding the
-
# result of the rendering. The output embedding writes it to the current template.
-
#
-
# But you don't have to restrict yourself to static includes. Templates can share variables amongst themselves by using instance
-
# variables defined using the regular embedding tags. Like this:
-
#
-
# <% @page_title = "A Wonderful Hello" %>
-
# <%= render "shared/header" %>
-
#
-
# Now the header can pick up on the <tt>@page_title</tt> variable and use it for outputting a title tag:
-
#
-
# <title><%= @page_title %></title>
-
#
-
# === Passing local variables to sub templates
-
#
-
# You can pass local variables to sub templates by using a hash with the variable names as keys and the objects as values:
-
#
-
# <%= render "shared/header", { headline: "Welcome", person: person } %>
-
#
-
# These can now be accessed in <tt>shared/header</tt> with:
-
#
-
# Headline: <%= headline %>
-
# First name: <%= person.first_name %>
-
#
-
# If you need to find out whether a certain local variable has been assigned a value in a particular render call,
-
# you need to use the following pattern:
-
#
-
# <% if local_assigns.has_key? :headline %>
-
# Headline: <%= headline %>
-
# <% end %>
-
#
-
# Testing using <tt>defined? headline</tt> will not work. This is an implementation restriction.
-
#
-
# === Template caching
-
#
-
# By default, Rails will compile each template to a method in order to render it. When you alter a template,
-
# Rails will check the file's modification time and recompile it in development mode.
-
#
-
# == Builder
-
#
-
# Builder templates are a more programmatic alternative to ERB. They are especially useful for generating XML content. An XmlMarkup object
-
# named +xml+ is automatically made available to templates with a <tt>.builder</tt> extension.
-
#
-
# Here are some basic examples:
-
#
-
# xml.em("emphasized") # => <em>emphasized</em>
-
# xml.em { xml.b("emph & bold") } # => <em><b>emph & bold</b></em>
-
# xml.a("A Link", "href" => "http://onestepback.org") # => <a href="http://onestepback.org">A Link</a>
-
# xml.target("name" => "compile", "option" => "fast") # => <target option="fast" name="compile"\>
-
# # NOTE: order of attributes is not specified.
-
#
-
# Any method with a block will be treated as an XML markup tag with nested markup in the block. For example, the following:
-
#
-
# xml.div do
-
# xml.h1(@person.name)
-
# xml.p(@person.bio)
-
# end
-
#
-
# would produce something like:
-
#
-
# <div>
-
# <h1>David Heinemeier Hansson</h1>
-
# <p>A product of Danish Design during the Winter of '79...</p>
-
# </div>
-
#
-
# A full-length RSS example actually used on Basecamp:
-
#
-
# xml.rss("version" => "2.0", "xmlns:dc" => "http://purl.org/dc/elements/1.1/") do
-
# xml.channel do
-
# xml.title(@feed_title)
-
# xml.link(@url)
-
# xml.description "Basecamp: Recent items"
-
# xml.language "en-us"
-
# xml.ttl "40"
-
#
-
# @recent_items.each do |item|
-
# xml.item do
-
# xml.title(item_title(item))
-
# xml.description(item_description(item)) if item_description(item)
-
# xml.pubDate(item_pubDate(item))
-
# xml.guid(@person.firm.account.url + @recent_items.url(item))
-
# xml.link(@person.firm.account.url + @recent_items.url(item))
-
#
-
# xml.tag!("dc:creator", item.author_name) if item_has_creator?(item)
-
# end
-
# end
-
# end
-
# end
-
#
-
# More builder documentation can be found at http://builder.rubyforge.org.
-
1
class Base
-
1
include Helpers, ::ERB::Util, Context
-
-
# Specify the proc used to decorate input tags that refer to attributes with errors.
-
1
cattr_accessor :field_error_proc
-
1
@@field_error_proc = Proc.new{ |html_tag, instance| "<div class=\"field_with_errors\">#{html_tag}</div>".html_safe }
-
-
# How to complete the streaming when an exception occurs.
-
# This is our best guess: first try to close the attribute, then the tag.
-
1
cattr_accessor :streaming_completion_on_exception
-
1
@@streaming_completion_on_exception = %("><script>window.location = "/500.html"</script></html>)
-
-
# Specify whether rendering within namespaced controllers should prefix
-
# the partial paths for ActiveModel objects with the namespace.
-
# (e.g., an Admin::PostsController would render @post using /admin/posts/_post.erb)
-
1
cattr_accessor :prefix_partial_path_with_controller_namespace
-
1
@@prefix_partial_path_with_controller_namespace = true
-
-
# Specify default_formats that can be rendered.
-
1
cattr_accessor :default_formats
-
-
1
class_attribute :_routes
-
1
class_attribute :logger
-
-
1
class << self
-
1
delegate :erb_trim_mode=, :to => 'ActionView::Template::Handlers::ERB'
-
-
1
def cache_template_loading
-
ActionView::Resolver.caching?
-
end
-
-
1
def cache_template_loading=(value)
-
ActionView::Resolver.caching = value
-
end
-
-
1
def xss_safe? #:nodoc:
-
true
-
end
-
end
-
-
1
attr_accessor :view_renderer
-
1
attr_internal :config, :assigns
-
-
1
delegate :lookup_context, :to => :view_renderer
-
1
delegate :formats, :formats=, :locale, :locale=, :view_paths, :view_paths=, :to => :lookup_context
-
-
1
def assign(new_assigns) # :nodoc:
-
432
@_assigns = new_assigns.each { |key, value| instance_variable_set("@#{key}", value) }
-
end
-
-
1
def initialize(context = nil, assigns = {}, controller = nil, formats = nil) #:nodoc:
-
137
@_config = ActiveSupport::InheritableOptions.new
-
-
137
if context.is_a?(ActionView::Renderer)
-
137
@view_renderer = context
-
else
-
lookup_context = context.is_a?(ActionView::LookupContext) ?
-
context : ActionView::LookupContext.new(context)
-
lookup_context.formats = formats if formats
-
lookup_context.prefixes = controller._prefixes if controller
-
@view_renderer = ActionView::Renderer.new(lookup_context)
-
end
-
-
137
assign(assigns)
-
137
assign_controller(controller)
-
137
_prepare_context
-
end
-
-
1
ActiveSupport.run_load_hooks(:action_view, self)
-
end
-
end
-
1
require 'active_support/core_ext/string/output_safety'
-
-
1
module ActionView
-
1
class OutputBuffer < ActiveSupport::SafeBuffer #:nodoc:
-
1
def initialize(*)
-
133
super
-
133
encode!
-
end
-
-
1
def <<(value)
-
46
super(value.to_s)
-
end
-
1
alias :append= :<<
-
1
alias :safe_append= :safe_concat
-
end
-
-
1
class StreamingBuffer #:nodoc:
-
1
def initialize(block)
-
@block = block
-
end
-
-
1
def <<(value)
-
value = value.to_s
-
value = ERB::Util.h(value) unless value.html_safe?
-
@block.call(value)
-
end
-
1
alias :concat :<<
-
1
alias :append= :<<
-
-
1
def safe_concat(value)
-
@block.call(value.to_s)
-
end
-
1
alias :safe_append= :safe_concat
-
-
1
def html_safe?
-
true
-
end
-
-
1
def html_safe
-
self
-
end
-
end
-
end
-
1
module ActionView
-
1
module CompiledTemplates #:nodoc:
-
# holds compiled template code
-
end
-
-
# = Action View Context
-
#
-
# Action View contexts are supplied to Action Controller to render a template.
-
# The default Action View context is ActionView::Base.
-
#
-
# In order to work with ActionController, a Context must just include this module.
-
# The initialization of the variables used by the context (@output_buffer, @view_flow,
-
# and @virtual_path) is responsibility of the object that includes this module
-
# (although you can call _prepare_context defined below).
-
1
module Context
-
1
include CompiledTemplates
-
1
attr_accessor :output_buffer, :view_flow
-
-
# Prepares the context by setting the appropriate instance variables.
-
# :api: plugin
-
1
def _prepare_context
-
137
@view_flow = OutputFlow.new
-
137
@output_buffer = nil
-
137
@virtual_path = nil
-
end
-
-
# Encapsulates the interaction with the view flow so it
-
# returns the correct buffer on +yield+. This is usually
-
# overwritten by helpers to add more behavior.
-
# :api: plugin
-
1
def _layout_for(name=nil)
-
9
name ||= :layout
-
9
view_flow.get(name).html_safe
-
end
-
end
-
end
-
1
require 'active_support/core_ext/string/output_safety'
-
-
1
module ActionView
-
1
class OutputFlow #:nodoc:
-
1
attr_reader :content
-
-
1
def initialize
-
137
@content = Hash.new { |h,k| h[k] = ActiveSupport::SafeBuffer.new }
-
end
-
-
# Called by _layout_for to read stored values.
-
1
def get(key)
-
9
@content[key]
-
end
-
-
# Called by each renderer object to set the layout contents.
-
1
def set(key, value)
-
9
@content[key] = value
-
end
-
-
# Called by content_for
-
1
def append(key, value)
-
@content[key] << value
-
end
-
1
alias_method :append!, :append
-
-
end
-
-
1
class StreamingFlow < OutputFlow #:nodoc:
-
1
def initialize(view, fiber)
-
@view = view
-
@parent = nil
-
@child = view.output_buffer
-
@content = view.view_flow.content
-
@fiber = fiber
-
@root = Fiber.current.object_id
-
end
-
-
# Try to get an stored content. If the content
-
# is not available and we are inside the layout
-
# fiber, we set that we are waiting for the given
-
# key and yield.
-
1
def get(key)
-
return super if @content.key?(key)
-
-
if inside_fiber?
-
view = @view
-
-
begin
-
@waiting_for = key
-
view.output_buffer, @parent = @child, view.output_buffer
-
Fiber.yield
-
ensure
-
@waiting_for = nil
-
view.output_buffer, @child = @parent, view.output_buffer
-
end
-
end
-
-
super
-
end
-
-
# Appends the contents for the given key. This is called
-
# by provides and resumes back to the fiber if it is
-
# the key it is waiting for.
-
1
def append!(key, value)
-
super
-
@fiber.resume if @waiting_for == key
-
end
-
-
1
private
-
-
1
def inside_fiber?
-
Fiber.current.object_id != @root
-
end
-
end
-
end
-
1
module ActionView #:nodoc:
-
1
module Helpers #:nodoc:
-
1
extend ActiveSupport::Autoload
-
-
1
autoload :ActiveModelHelper
-
1
autoload :AssetTagHelper
-
1
autoload :AssetUrlHelper
-
1
autoload :AtomFeedHelper
-
1
autoload :BenchmarkHelper
-
1
autoload :CacheHelper
-
1
autoload :CaptureHelper
-
1
autoload :ControllerHelper
-
1
autoload :CsrfHelper
-
1
autoload :DateHelper
-
1
autoload :DebugHelper
-
1
autoload :FormHelper
-
1
autoload :FormOptionsHelper
-
1
autoload :FormTagHelper
-
1
autoload :JavaScriptHelper, "action_view/helpers/javascript_helper"
-
1
autoload :NumberHelper
-
1
autoload :OutputSafetyHelper
-
1
autoload :RecordTagHelper
-
1
autoload :RenderingHelper
-
1
autoload :SanitizeHelper
-
1
autoload :TagHelper
-
1
autoload :TextHelper
-
1
autoload :TranslationHelper
-
1
autoload :UrlHelper
-
-
1
extend ActiveSupport::Concern
-
-
1
include ActiveModelHelper
-
1
include AssetTagHelper
-
1
include AssetUrlHelper
-
1
include AtomFeedHelper
-
1
include BenchmarkHelper
-
1
include CacheHelper
-
1
include CaptureHelper
-
1
include ControllerHelper
-
1
include CsrfHelper
-
1
include DateHelper
-
1
include DebugHelper
-
1
include FormHelper
-
1
include FormOptionsHelper
-
1
include FormTagHelper
-
1
include JavaScriptHelper
-
1
include NumberHelper
-
1
include OutputSafetyHelper
-
1
include RecordTagHelper
-
1
include RenderingHelper
-
1
include SanitizeHelper
-
1
include TagHelper
-
1
include TextHelper
-
1
include TranslationHelper
-
1
include UrlHelper
-
end
-
end
-
1
require 'active_support/core_ext/class/attribute_accessors'
-
1
require 'active_support/core_ext/enumerable'
-
-
1
module ActionView
-
# = Active Model Helpers
-
1
module Helpers
-
1
module ActiveModelHelper
-
end
-
-
1
module ActiveModelInstanceTag
-
1
def object
-
@active_model_object ||= begin
-
object = super
-
object.respond_to?(:to_model) ? object.to_model : object
-
end
-
end
-
-
1
def content_tag(*)
-
error_wrapping(super)
-
end
-
-
1
def tag(type, options, *)
-
tag_generate_errors?(options) ? error_wrapping(super) : super
-
end
-
-
1
def error_wrapping(html_tag)
-
if object_has_errors?
-
Base.field_error_proc.call(html_tag, self)
-
else
-
html_tag
-
end
-
end
-
-
1
def error_message
-
object.errors[@method_name]
-
end
-
-
1
private
-
-
1
def object_has_errors?
-
object.respond_to?(:errors) && object.errors.respond_to?(:[]) && error_message.present?
-
end
-
-
1
def tag_generate_errors?(options)
-
options['type'] != 'hidden'
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/array/extract_options'
-
1
require 'active_support/core_ext/hash/keys'
-
1
require 'action_view/helpers/asset_url_helper'
-
1
require 'action_view/helpers/tag_helper'
-
-
1
module ActionView
-
# = Action View Asset Tag Helpers
-
1
module Helpers #:nodoc:
-
# This module provides methods for generating HTML that links views to assets such
-
# as images, javascripts, stylesheets, and feeds. These methods do not verify
-
# the assets exist before linking to them:
-
#
-
# image_tag("rails.png")
-
# # => <img alt="Rails" src="/assets/rails.png" />
-
# stylesheet_link_tag("application")
-
# # => <link href="/assets/application.css?body=1" media="screen" rel="stylesheet" />
-
#
-
1
module AssetTagHelper
-
1
extend ActiveSupport::Concern
-
-
1
include AssetUrlHelper
-
1
include TagHelper
-
-
# Returns an HTML script tag for each of the +sources+ provided.
-
#
-
# Sources may be paths to JavaScript files. Relative paths are assumed to be relative
-
# to <tt>assets/javascripts</tt>, full paths are assumed to be relative to the document
-
# root. Relative paths are idiomatic, use absolute paths only when needed.
-
#
-
# When passing paths, the ".js" extension is optional.
-
#
-
# You can modify the HTML attributes of the script tag by passing a hash as the
-
# last argument.
-
#
-
# javascript_include_tag "xmlhr"
-
# # => <script src="/assets/xmlhr.js?1284139606"></script>
-
#
-
# javascript_include_tag "xmlhr.js"
-
# # => <script src="/assets/xmlhr.js?1284139606"></script>
-
#
-
# javascript_include_tag "common.javascript", "/elsewhere/cools"
-
# # => <script src="/assets/common.javascript?1284139606"></script>
-
# # <script src="/elsewhere/cools.js?1423139606"></script>
-
#
-
# javascript_include_tag "http://www.example.com/xmlhr"
-
# # => <script src="http://www.example.com/xmlhr"></script>
-
#
-
# javascript_include_tag "http://www.example.com/xmlhr.js"
-
# # => <script src="http://www.example.com/xmlhr.js"></script>
-
#
-
1
def javascript_include_tag(*sources)
-
options = sources.extract_options!.stringify_keys
-
sources.uniq.map { |source|
-
tag_options = {
-
"src" => path_to_javascript(source)
-
}.merge(options)
-
content_tag(:script, "", tag_options)
-
}.join("\n").html_safe
-
end
-
-
# Returns a stylesheet link tag for the sources specified as arguments. If
-
# you don't specify an extension, <tt>.css</tt> will be appended automatically.
-
# You can modify the link attributes by passing a hash as the last argument.
-
# For historical reasons, the 'media' attribute will always be present and defaults
-
# to "screen", so you must explicitely set it to "all" for the stylesheet(s) to
-
# apply to all media types.
-
#
-
# stylesheet_link_tag "style"
-
# # => <link href="/assets/style.css" media="screen" rel="stylesheet" />
-
#
-
# stylesheet_link_tag "style.css"
-
# # => <link href="/assets/style.css" media="screen" rel="stylesheet" />
-
#
-
# stylesheet_link_tag "http://www.example.com/style.css"
-
# # => <link href="http://www.example.com/style.css" media="screen" rel="stylesheet" />
-
#
-
# stylesheet_link_tag "style", media: "all"
-
# # => <link href="/assets/style.css" media="all" rel="stylesheet" />
-
#
-
# stylesheet_link_tag "style", media: "print"
-
# # => <link href="/assets/style.css" media="print" rel="stylesheet" />
-
#
-
# stylesheet_link_tag "random.styles", "/css/stylish"
-
# # => <link href="/assets/random.styles" media="screen" rel="stylesheet" />
-
# # <link href="/css/stylish.css" media="screen" rel="stylesheet" />
-
#
-
1
def stylesheet_link_tag(*sources)
-
options = sources.extract_options!.stringify_keys
-
sources.uniq.map { |source|
-
tag_options = {
-
"rel" => "stylesheet",
-
"media" => "screen",
-
"href" => path_to_stylesheet(source)
-
}.merge(options)
-
tag(:link, tag_options)
-
}.join("\n").html_safe
-
end
-
-
# Returns a link tag that browsers and news readers can use to auto-detect
-
# an RSS or Atom feed. The +type+ can either be <tt>:rss</tt> (default) or
-
# <tt>:atom</tt>. Control the link options in url_for format using the
-
# +url_options+. You can modify the LINK tag itself in +tag_options+.
-
#
-
# ==== Options
-
# * <tt>:rel</tt> - Specify the relation of this link, defaults to "alternate"
-
# * <tt>:type</tt> - Override the auto-generated mime type
-
# * <tt>:title</tt> - Specify the title of the link, defaults to the +type+
-
#
-
# ==== Examples
-
# auto_discovery_link_tag
-
# # => <link rel="alternate" type="application/rss+xml" title="RSS" href="http://www.currenthost.com/controller/action" />
-
# auto_discovery_link_tag(:atom)
-
# # => <link rel="alternate" type="application/atom+xml" title="ATOM" href="http://www.currenthost.com/controller/action" />
-
# auto_discovery_link_tag(:rss, {action: "feed"})
-
# # => <link rel="alternate" type="application/rss+xml" title="RSS" href="http://www.currenthost.com/controller/feed" />
-
# auto_discovery_link_tag(:rss, {action: "feed"}, {title: "My RSS"})
-
# # => <link rel="alternate" type="application/rss+xml" title="My RSS" href="http://www.currenthost.com/controller/feed" />
-
# auto_discovery_link_tag(:rss, {controller: "news", action: "feed"})
-
# # => <link rel="alternate" type="application/rss+xml" title="RSS" href="http://www.currenthost.com/news/feed" />
-
# auto_discovery_link_tag(:rss, "http://www.example.com/feed.rss", {title: "Example RSS"})
-
# # => <link rel="alternate" type="application/rss+xml" title="Example RSS" href="http://www.example.com/feed" />
-
1
def auto_discovery_link_tag(type = :rss, url_options = {}, tag_options = {})
-
if !(type == :rss || type == :atom) && tag_options[:type].blank?
-
message = "You have passed type other than :rss or :atom to auto_discovery_link_tag and haven't supplied " +
-
"the :type option key. This behavior is deprecated and will be remove in Rails 4.1. You should pass " +
-
":type option explicitly if you want to use other types, for example: " +
-
"auto_discovery_link_tag(:xml, '/feed.xml', :type => 'application/xml')"
-
ActiveSupport::Deprecation.warn message
-
end
-
-
tag(
-
"link",
-
"rel" => tag_options[:rel] || "alternate",
-
"type" => tag_options[:type] || Mime::Type.lookup_by_extension(type.to_s).to_s,
-
"title" => tag_options[:title] || type.to_s.upcase,
-
"href" => url_options.is_a?(Hash) ? url_for(url_options.merge(:only_path => false)) : url_options
-
)
-
end
-
-
# <%= favicon_link_tag %>
-
#
-
# generates
-
#
-
# <link href="/assets/favicon.ico" rel="shortcut icon" type="image/vnd.microsoft.icon" />
-
#
-
# You may specify a different file in the first argument:
-
#
-
# <%= favicon_link_tag '/myicon.ico' %>
-
#
-
# That's passed to +path_to_image+ as is, so it gives
-
#
-
# <link href="/myicon.ico" rel="shortcut icon" type="image/vnd.microsoft.icon" />
-
#
-
# The helper accepts an additional options hash where you can override "rel" and "type".
-
#
-
# For example, Mobile Safari looks for a different LINK tag, pointing to an image that
-
# will be used if you add the page to the home screen of an iPod Touch, iPhone, or iPad.
-
# The following call would generate such a tag:
-
#
-
# <%= favicon_link_tag 'mb-icon.png', rel: 'apple-touch-icon', type: 'image/png' %>
-
1
def favicon_link_tag(source='favicon.ico', options={})
-
tag('link', {
-
:rel => 'shortcut icon',
-
:type => 'image/vnd.microsoft.icon',
-
:href => path_to_image(source)
-
}.merge(options.symbolize_keys))
-
end
-
-
# Returns an html image tag for the +source+. The +source+ can be a full
-
# path or a file.
-
#
-
# ==== Options
-
# You can add HTML attributes using the +options+. The +options+ supports
-
# three additional keys for convenience and conformance:
-
#
-
# * <tt>:alt</tt> - If no alt text is given, the file name part of the
-
# +source+ is used (capitalized and without the extension)
-
# * <tt>:size</tt> - Supplied as "{Width}x{Height}" or "{Number}", so "30x45" becomes
-
# width="30" and height="45", and "50" becomes width="50" and height="50".
-
# <tt>:size</tt> will be ignored if the value is not in the correct format.
-
#
-
# image_tag("icon")
-
# # => <img alt="Icon" src="/assets/icon" />
-
# image_tag("icon.png")
-
# # => <img alt="Icon" src="/assets/icon.png" />
-
# image_tag("icon.png", size: "16x10", alt: "Edit Entry")
-
# # => <img src="/assets/icon.png" width="16" height="10" alt="Edit Entry" />
-
# image_tag("/icons/icon.gif", size: "16")
-
# # => <img src="/icons/icon.gif" width="16" height="16" alt="Icon" />
-
# image_tag("/icons/icon.gif", height: '32', width: '32')
-
# # => <img alt="Icon" height="32" src="/icons/icon.gif" width="32" />
-
# image_tag("/icons/icon.gif", class: "menu_icon")
-
# # => <img alt="Icon" class="menu_icon" src="/icons/icon.gif" />
-
1
def image_tag(source, options={})
-
8
options = options.symbolize_keys
-
-
8
src = options[:src] = path_to_image(source)
-
-
8
unless src =~ /^(?:cid|data):/ || src.blank?
-
14
options[:alt] = options.fetch(:alt){ image_alt(src) }
-
end
-
-
8
if size = options.delete(:size)
-
options[:width], options[:height] = size.split("x") if size =~ %r{\A\d+x\d+\z}
-
options[:width] = options[:height] = size if size =~ %r{\A\d+\z}
-
end
-
-
8
tag("img", options)
-
end
-
-
1
def image_alt(src)
-
7
File.basename(src, '.*').sub(/-[[:xdigit:]]{32}\z/, '').capitalize
-
end
-
-
# Returns an html video tag for the +sources+. If +sources+ is a string,
-
# a single video tag will be returned. If +sources+ is an array, a video
-
# tag with nested source tags for each source will be returned. The
-
# +sources+ can be full paths or files that exists in your public videos
-
# directory.
-
#
-
# ==== Options
-
# You can add HTML attributes using the +options+. The +options+ supports
-
# two additional keys for convenience and conformance:
-
#
-
# * <tt>:poster</tt> - Set an image (like a screenshot) to be shown
-
# before the video loads. The path is calculated like the +src+ of +image_tag+.
-
# * <tt>:size</tt> - Supplied as "{Width}x{Height}", so "30x45" becomes
-
# width="30" and height="45". <tt>:size</tt> will be ignored if the
-
# value is not in the correct format.
-
#
-
# video_tag("trailer")
-
# # => <video src="/videos/trailer" />
-
# video_tag("trailer.ogg")
-
# # => <video src="/videos/trailer.ogg" />
-
# video_tag("trailer.ogg", controls: true, autobuffer: true)
-
# # => <video autobuffer="autobuffer" controls="controls" src="/videos/trailer.ogg" />
-
# video_tag("trailer.m4v", size: "16x10", poster: "screenshot.png")
-
# # => <video src="/videos/trailer.m4v" width="16" height="10" poster="/assets/screenshot.png" />
-
# video_tag("/trailers/hd.avi", size: "16x16")
-
# # => <video src="/trailers/hd.avi" width="16" height="16" />
-
# video_tag("/trailers/hd.avi", height: '32', width: '32')
-
# # => <video height="32" src="/trailers/hd.avi" width="32" />
-
# video_tag("trailer.ogg", "trailer.flv")
-
# # => <video><source src="/videos/trailer.ogg" /><source src="/videos/trailer.flv" /></video>
-
# video_tag(["trailer.ogg", "trailer.flv"])
-
# # => <video><source src="/videos/trailer.ogg" /><source src="/videos/trailer.flv" /></video>
-
# video_tag(["trailer.ogg", "trailer.flv"], size: "160x120")
-
# # => <video height="120" width="160"><source src="/videos/trailer.ogg" /><source src="/videos/trailer.flv" /></video>
-
1
def video_tag(*sources)
-
multiple_sources_tag('video', sources) do |options|
-
options[:poster] = path_to_image(options[:poster]) if options[:poster]
-
-
if size = options.delete(:size)
-
options[:width], options[:height] = size.split("x") if size =~ %r{^\d+x\d+$}
-
end
-
end
-
end
-
-
# Returns an html audio tag for the +source+.
-
# The +source+ can be full path or file that exists in
-
# your public audios directory.
-
#
-
# audio_tag("sound") # =>
-
# <audio src="/audios/sound" />
-
# audio_tag("sound.wav") # =>
-
# <audio src="/audios/sound.wav" />
-
# audio_tag("sound.wav", autoplay: true, controls: true) # =>
-
# <audio autoplay="autoplay" controls="controls" src="/audios/sound.wav" />
-
# audio_tag("sound.wav", "sound.mid") # =>
-
# <audio><source src="/audios/sound.wav" /><source src="/audios/sound.mid" /></audio>
-
1
def audio_tag(*sources)
-
multiple_sources_tag('audio', sources)
-
end
-
-
1
private
-
1
def multiple_sources_tag(type, sources)
-
options = sources.extract_options!.symbolize_keys
-
sources.flatten!
-
-
yield options if block_given?
-
-
if sources.size > 1
-
content_tag(type, options) do
-
safe_join sources.map { |source| tag("source", :src => send("path_to_#{type}", source)) }
-
end
-
else
-
options[:src] = send("path_to_#{type}", sources.first)
-
content_tag(type, nil, options)
-
end
-
end
-
end
-
end
-
end
-
1
require 'zlib'
-
-
1
module ActionView
-
# = Action View Asset URL Helpers
-
1
module Helpers #:nodoc:
-
# This module provides methods for generating asset paths and
-
# urls.
-
#
-
# image_path("rails.png")
-
# # => "/assets/rails.png"
-
#
-
# image_url("rails.png")
-
# # => "http://www.example.com/assets/rails.png"
-
#
-
# === Using asset hosts
-
#
-
# By default, Rails links to these assets on the current host in the public
-
# folder, but you can direct Rails to link to assets from a dedicated asset
-
# server by setting <tt>ActionController::Base.asset_host</tt> in the application
-
# configuration, typically in <tt>config/environments/production.rb</tt>.
-
# For example, you'd define <tt>assets.example.com</tt> to be your asset
-
# host this way, inside the <tt>configure</tt> block of your environment-specific
-
# configuration files or <tt>config/application.rb</tt>:
-
#
-
# config.action_controller.asset_host = "assets.example.com"
-
#
-
# Helpers take that into account:
-
#
-
# image_tag("rails.png")
-
# # => <img alt="Rails" src="http://assets.example.com/assets/rails.png" />
-
# stylesheet_link_tag("application")
-
# # => <link href="http://assets.example.com/assets/application.css" media="screen" rel="stylesheet" />
-
#
-
# Browsers typically open at most two simultaneous connections to a single
-
# host, which means your assets often have to wait for other assets to finish
-
# downloading. You can alleviate this by using a <tt>%d</tt> wildcard in the
-
# +asset_host+. For example, "assets%d.example.com". If that wildcard is
-
# present Rails distributes asset requests among the corresponding four hosts
-
# "assets0.example.com", ..., "assets3.example.com". With this trick browsers
-
# will open eight simultaneous connections rather than two.
-
#
-
# image_tag("rails.png")
-
# # => <img alt="Rails" src="http://assets0.example.com/assets/rails.png" />
-
# stylesheet_link_tag("application")
-
# # => <link href="http://assets2.example.com/assets/application.css" media="screen" rel="stylesheet" />
-
#
-
# To do this, you can either setup four actual hosts, or you can use wildcard
-
# DNS to CNAME the wildcard to a single asset host. You can read more about
-
# setting up your DNS CNAME records from your ISP.
-
#
-
# Note: This is purely a browser performance optimization and is not meant
-
# for server load balancing. See http://www.die.net/musings/page_load_time/
-
# for background.
-
#
-
# Alternatively, you can exert more control over the asset host by setting
-
# +asset_host+ to a proc like this:
-
#
-
# ActionController::Base.asset_host = Proc.new { |source|
-
# "http://assets#{Digest::MD5.hexdigest(source).to_i(16) % 2 + 1}.example.com"
-
# }
-
# image_tag("rails.png")
-
# # => <img alt="Rails" src="http://assets1.example.com/assets/rails.png" />
-
# stylesheet_link_tag("application")
-
# # => <link href="http://assets2.example.com/assets/application.css" media="screen" rel="stylesheet" />
-
#
-
# The example above generates "http://assets1.example.com" and
-
# "http://assets2.example.com". This option is useful for example if
-
# you need fewer/more than four hosts, custom host names, etc.
-
#
-
# As you see the proc takes a +source+ parameter. That's a string with the
-
# absolute path of the asset, for example "/assets/rails.png".
-
#
-
# ActionController::Base.asset_host = Proc.new { |source|
-
# if source.ends_with?('.css')
-
# "http://stylesheets.example.com"
-
# else
-
# "http://assets.example.com"
-
# end
-
# }
-
# image_tag("rails.png")
-
# # => <img alt="Rails" src="http://assets.example.com/assets/rails.png" />
-
# stylesheet_link_tag("application")
-
# # => <link href="http://stylesheets.example.com/assets/application.css" media="screen" rel="stylesheet" />
-
#
-
# Alternatively you may ask for a second parameter +request+. That one is
-
# particularly useful for serving assets from an SSL-protected page. The
-
# example proc below disables asset hosting for HTTPS connections, while
-
# still sending assets for plain HTTP requests from asset hosts. If you don't
-
# have SSL certificates for each of the asset hosts this technique allows you
-
# to avoid warnings in the client about mixed media.
-
#
-
# config.action_controller.asset_host = Proc.new { |source, request|
-
# if request.ssl?
-
# "#{request.protocol}#{request.host_with_port}"
-
# else
-
# "#{request.protocol}assets.example.com"
-
# end
-
# }
-
#
-
# You can also implement a custom asset host object that responds to +call+
-
# and takes either one or two parameters just like the proc.
-
#
-
# config.action_controller.asset_host = AssetHostingWithMinimumSsl.new(
-
# "http://asset%d.example.com", "https://asset1.example.com"
-
# )
-
#
-
1
module AssetUrlHelper
-
1
URI_REGEXP = %r{^[-a-z]+://|^(?:cid|data):|^//}
-
-
# Computes the path to asset in public directory. If :type
-
# options is set, a file extension will be appended and scoped
-
# to the corresponding public directory.
-
#
-
# All other asset *_path helpers delegate through this method.
-
#
-
# asset_path "application.js" # => /application.js
-
# asset_path "application", type: :javascript # => /javascripts/application.js
-
# asset_path "application", type: :stylesheet # => /stylesheets/application.css
-
# asset_path "http://www.example.com/js/xmlhr.js" # => http://www.example.com/js/xmlhr.js
-
1
def asset_path(source, options = {})
-
8
source = source.to_s
-
8
return "" unless source.present?
-
8
return source if source =~ URI_REGEXP
-
-
7
tail, source = source[/([\?#].+)$/], source.sub(/([\?#].+)$/, '')
-
-
7
if extname = compute_asset_extname(source, options)
-
source = "#{source}#{extname}"
-
end
-
-
7
if source[0] != ?/
-
7
source = compute_asset_path(source, options)
-
end
-
-
7
relative_url_root = (defined?(config.relative_url_root) && config.relative_url_root) ||
-
7
(respond_to?(:request) && request.try(:script_name))
-
7
if relative_url_root
-
source = "#{relative_url_root}#{source}" unless source.starts_with?("#{relative_url_root}/")
-
end
-
-
7
if host = compute_asset_host(source, options)
-
5
source = "#{host}#{source}"
-
end
-
-
7
"#{source}#{tail}"
-
end
-
1
alias_method :path_to_asset, :asset_path # aliased to avoid conflicts with a asset_path named route
-
-
# Computes the full URL to a asset in the public directory. This
-
# will use +asset_path+ internally, so most of their behaviors
-
# will be the same.
-
1
def asset_url(source, options = {})
-
path_to_asset(source, options.merge(:protocol => :request))
-
end
-
1
alias_method :url_to_asset, :asset_url # aliased to avoid conflicts with an asset_url named route
-
-
1
ASSET_EXTENSIONS = {
-
javascript: '.js',
-
stylesheet: '.css'
-
}
-
-
# Compute extname to append to asset path. Returns nil if
-
# nothing should be added.
-
1
def compute_asset_extname(source, options = {})
-
7
return if options[:extname] == false
-
7
extname = options[:extname] || ASSET_EXTENSIONS[options[:type]]
-
7
extname if extname && File.extname(source) != extname
-
end
-
-
# Maps asset types to public directory.
-
1
ASSET_PUBLIC_DIRECTORIES = {
-
audio: '/audios',
-
font: '/fonts',
-
image: '/images',
-
javascript: '/javascripts',
-
stylesheet: '/stylesheets',
-
video: '/videos'
-
}
-
-
# Computes asset path to public directory. Plugins and
-
# extensions can override this method to point to custom assets
-
# or generate digested paths or query strings.
-
1
def compute_asset_path(source, options = {})
-
7
dir = ASSET_PUBLIC_DIRECTORIES[options[:type]] || ""
-
7
File.join(dir, source)
-
end
-
-
# Pick an asset host for this source. Returns +nil+ if no host is set,
-
# the host if no wildcard is set, the host interpolated with the
-
# numbers 0-3 if it contains <tt>%d</tt> (the number is the source hash mod 4),
-
# or the value returned from invoking call on an object responding to call
-
# (proc or otherwise).
-
1
def compute_asset_host(source = "", options = {})
-
7
request = self.request if respond_to?(:request)
-
7
host = config.asset_host if defined? config.asset_host
-
7
host ||= request.base_url if request && options[:protocol] == :request
-
7
return unless host
-
-
5
if host.respond_to?(:call)
-
1
arity = host.respond_to?(:arity) ? host.arity : host.method(:call).arity
-
1
args = [source]
-
1
args << request if request && (arity > 1 || arity < 0)
-
1
host = host.call(*args)
-
elsif host =~ /%d/
-
host = host % (Zlib.crc32(source) % 4)
-
end
-
-
5
if host =~ URI_REGEXP
-
5
host
-
else
-
protocol = options[:protocol] || config.default_asset_host_protocol || (request ? :request : :relative)
-
case protocol
-
when :relative
-
"//#{host}"
-
when :request
-
"#{request.protocol}#{host}"
-
else
-
"#{protocol}://#{host}"
-
end
-
end
-
end
-
-
# Computes the path to a javascript asset in the public javascripts directory.
-
# If the +source+ filename has no extension, .js will be appended (except for explicit URIs)
-
# Full paths from the document root will be passed through.
-
# Used internally by javascript_include_tag to build the script path.
-
#
-
# javascript_path "xmlhr" # => /javascripts/xmlhr.js
-
# javascript_path "dir/xmlhr.js" # => /javascripts/dir/xmlhr.js
-
# javascript_path "/dir/xmlhr" # => /dir/xmlhr.js
-
# javascript_path "http://www.example.com/js/xmlhr" # => http://www.example.com/js/xmlhr
-
# javascript_path "http://www.example.com/js/xmlhr.js" # => http://www.example.com/js/xmlhr.js
-
1
def javascript_path(source, options = {})
-
path_to_asset(source, {type: :javascript}.merge!(options))
-
end
-
1
alias_method :path_to_javascript, :javascript_path # aliased to avoid conflicts with a javascript_path named route
-
-
# Computes the full URL to a javascript asset in the public javascripts directory.
-
# This will use +javascript_path+ internally, so most of their behaviors will be the same.
-
1
def javascript_url(source, options = {})
-
url_to_asset(source, {type: :javascript}.merge!(options))
-
end
-
1
alias_method :url_to_javascript, :javascript_url # aliased to avoid conflicts with a javascript_url named route
-
-
# Computes the path to a stylesheet asset in the public stylesheets directory.
-
# If the +source+ filename has no extension, <tt>.css</tt> will be appended (except for explicit URIs).
-
# Full paths from the document root will be passed through.
-
# Used internally by +stylesheet_link_tag+ to build the stylesheet path.
-
#
-
# stylesheet_path "style" # => /stylesheets/style.css
-
# stylesheet_path "dir/style.css" # => /stylesheets/dir/style.css
-
# stylesheet_path "/dir/style.css" # => /dir/style.css
-
# stylesheet_path "http://www.example.com/css/style" # => http://www.example.com/css/style
-
# stylesheet_path "http://www.example.com/css/style.css" # => http://www.example.com/css/style.css
-
1
def stylesheet_path(source, options = {})
-
path_to_asset(source, {type: :stylesheet}.merge!(options))
-
end
-
1
alias_method :path_to_stylesheet, :stylesheet_path # aliased to avoid conflicts with a stylesheet_path named route
-
-
# Computes the full URL to a stylesheet asset in the public stylesheets directory.
-
# This will use +stylesheet_path+ internally, so most of their behaviors will be the same.
-
1
def stylesheet_url(source, options = {})
-
url_to_asset(source, {type: :stylesheet}.merge!(options))
-
end
-
1
alias_method :url_to_stylesheet, :stylesheet_url # aliased to avoid conflicts with a stylesheet_url named route
-
-
# Computes the path to an image asset.
-
# Full paths from the document root will be passed through.
-
# Used internally by +image_tag+ to build the image path:
-
#
-
# image_path("edit") # => "/assets/edit"
-
# image_path("edit.png") # => "/assets/edit.png"
-
# image_path("icons/edit.png") # => "/assets/icons/edit.png"
-
# image_path("/icons/edit.png") # => "/icons/edit.png"
-
# image_path("http://www.example.com/img/edit.png") # => "http://www.example.com/img/edit.png"
-
#
-
# If you have images as application resources this method may conflict with their named routes.
-
# The alias +path_to_image+ is provided to avoid that. Rails uses the alias internally, and
-
# plugin authors are encouraged to do so.
-
1
def image_path(source, options = {})
-
8
path_to_asset(source, {type: :image}.merge!(options))
-
end
-
1
alias_method :path_to_image, :image_path # aliased to avoid conflicts with an image_path named route
-
-
# Computes the full URL to an image asset.
-
# This will use +image_path+ internally, so most of their behaviors will be the same.
-
1
def image_url(source, options = {})
-
url_to_asset(source, {type: :image}.merge!(options))
-
end
-
1
alias_method :url_to_image, :image_url # aliased to avoid conflicts with an image_url named route
-
-
# Computes the path to a video asset in the public videos directory.
-
# Full paths from the document root will be passed through.
-
# Used internally by +video_tag+ to build the video path.
-
#
-
# video_path("hd") # => /videos/hd
-
# video_path("hd.avi") # => /videos/hd.avi
-
# video_path("trailers/hd.avi") # => /videos/trailers/hd.avi
-
# video_path("/trailers/hd.avi") # => /trailers/hd.avi
-
# video_path("http://www.example.com/vid/hd.avi") # => http://www.example.com/vid/hd.avi
-
1
def video_path(source, options = {})
-
path_to_asset(source, {type: :video}.merge!(options))
-
end
-
1
alias_method :path_to_video, :video_path # aliased to avoid conflicts with a video_path named route
-
-
# Computes the full URL to a video asset in the public videos directory.
-
# This will use +video_path+ internally, so most of their behaviors will be the same.
-
1
def video_url(source, options = {})
-
url_to_asset(source, {type: :video}.merge!(options))
-
end
-
1
alias_method :url_to_video, :video_url # aliased to avoid conflicts with an video_url named route
-
-
# Computes the path to an audio asset in the public audios directory.
-
# Full paths from the document root will be passed through.
-
# Used internally by +audio_tag+ to build the audio path.
-
#
-
# audio_path("horse") # => /audios/horse
-
# audio_path("horse.wav") # => /audios/horse.wav
-
# audio_path("sounds/horse.wav") # => /audios/sounds/horse.wav
-
# audio_path("/sounds/horse.wav") # => /sounds/horse.wav
-
# audio_path("http://www.example.com/sounds/horse.wav") # => http://www.example.com/sounds/horse.wav
-
1
def audio_path(source, options = {})
-
path_to_asset(source, {type: :audio}.merge!(options))
-
end
-
1
alias_method :path_to_audio, :audio_path # aliased to avoid conflicts with an audio_path named route
-
-
# Computes the full URL to an audio asset in the public audios directory.
-
# This will use +audio_path+ internally, so most of their behaviors will be the same.
-
1
def audio_url(source, options = {})
-
url_to_asset(source, {type: :audio}.merge!(options))
-
end
-
1
alias_method :url_to_audio, :audio_url # aliased to avoid conflicts with an audio_url named route
-
-
# Computes the path to a font asset.
-
# Full paths from the document root will be passed through.
-
#
-
# font_path("font") # => /assets/font
-
# font_path("font.ttf") # => /assets/font.ttf
-
# font_path("dir/font.ttf") # => /assets/dir/font.ttf
-
# font_path("/dir/font.ttf") # => /dir/font.ttf
-
# font_path("http://www.example.com/dir/font.ttf") # => http://www.example.com/dir/font.ttf
-
1
def font_path(source, options = {})
-
path_to_asset(source, {type: :font}.merge!(options))
-
end
-
1
alias_method :path_to_font, :font_path # aliased to avoid conflicts with an font_path named route
-
-
# Computes the full URL to a font asset.
-
# This will use +font_path+ internally, so most of their behaviors will be the same.
-
1
def font_url(source, options = {})
-
url_to_asset(source, {type: :font}.merge!(options))
-
end
-
1
alias_method :url_to_font, :font_url # aliased to avoid conflicts with an font_url named route
-
end
-
end
-
end
-
1
require 'set'
-
-
1
module ActionView
-
# = Action View Atom Feed Helpers
-
1
module Helpers #:nodoc:
-
1
module AtomFeedHelper
-
# Adds easy defaults to writing Atom feeds with the Builder template engine (this does not work on ERB or any other
-
# template languages).
-
#
-
# Full usage example:
-
#
-
# config/routes.rb:
-
# Basecamp::Application.routes.draw do
-
# resources :posts
-
# root to: "posts#index"
-
# end
-
#
-
# app/controllers/posts_controller.rb:
-
# class PostsController < ApplicationController::Base
-
# # GET /posts.html
-
# # GET /posts.atom
-
# def index
-
# @posts = Post.all
-
#
-
# respond_to do |format|
-
# format.html
-
# format.atom
-
# end
-
# end
-
# end
-
#
-
# app/views/posts/index.atom.builder:
-
# atom_feed do |feed|
-
# feed.title("My great blog!")
-
# feed.updated(@posts[0].created_at) if @posts.length > 0
-
#
-
# @posts.each do |post|
-
# feed.entry(post) do |entry|
-
# entry.title(post.title)
-
# entry.content(post.body, type: 'html')
-
#
-
# entry.author do |author|
-
# author.name("DHH")
-
# end
-
# end
-
# end
-
# end
-
#
-
# The options for atom_feed are:
-
#
-
# * <tt>:language</tt>: Defaults to "en-US".
-
# * <tt>:root_url</tt>: The HTML alternative that this feed is doubling for. Defaults to / on the current host.
-
# * <tt>:url</tt>: The URL for this feed. Defaults to the current URL.
-
# * <tt>:id</tt>: The id for this feed. Defaults to "tag:#{request.host},#{options[:schema_date]}:#{request.fullpath.split(".")[0]}"
-
# * <tt>:schema_date</tt>: The date at which the tag scheme for the feed was first used. A good default is the year you
-
# created the feed. See http://feedvalidator.org/docs/error/InvalidTAG.html for more information. If not specified,
-
# 2005 is used (as an "I don't care" value).
-
# * <tt>:instruct</tt>: Hash of XML processing instructions in the form {target => {attribute => value, }} or {target => [{attribute => value, }, ]}
-
#
-
# Other namespaces can be added to the root element:
-
#
-
# app/views/posts/index.atom.builder:
-
# atom_feed({'xmlns:app' => 'http://www.w3.org/2007/app',
-
# 'xmlns:openSearch' => 'http://a9.com/-/spec/opensearch/1.1/'}) do |feed|
-
# feed.title("My great blog!")
-
# feed.updated((@posts.first.created_at))
-
# feed.tag!(openSearch:totalResults, 10)
-
#
-
# @posts.each do |post|
-
# feed.entry(post) do |entry|
-
# entry.title(post.title)
-
# entry.content(post.body, type: 'html')
-
# entry.tag!('app:edited', Time.now)
-
#
-
# entry.author do |author|
-
# author.name("DHH")
-
# end
-
# end
-
# end
-
# end
-
#
-
# The Atom spec defines five elements (content rights title subtitle
-
# summary) which may directly contain xhtml content if type: 'xhtml'
-
# is specified as an attribute. If so, this helper will take care of
-
# the enclosing div and xhtml namespace declaration. Example usage:
-
#
-
# entry.summary type: 'xhtml' do |xhtml|
-
# xhtml.p pluralize(order.line_items.count, "line item")
-
# xhtml.p "Shipped to #{order.address}"
-
# xhtml.p "Paid by #{order.pay_type}"
-
# end
-
#
-
#
-
# <tt>atom_feed</tt> yields an +AtomFeedBuilder+ instance. Nested elements yield
-
# an +AtomBuilder+ instance.
-
1
def atom_feed(options = {}, &block)
-
if options[:schema_date]
-
options[:schema_date] = options[:schema_date].strftime("%Y-%m-%d") if options[:schema_date].respond_to?(:strftime)
-
else
-
options[:schema_date] = "2005" # The Atom spec copyright date
-
end
-
-
xml = options.delete(:xml) || eval("xml", block.binding)
-
xml.instruct!
-
if options[:instruct]
-
options[:instruct].each do |target,attrs|
-
if attrs.respond_to?(:keys)
-
xml.instruct!(target, attrs)
-
elsif attrs.respond_to?(:each)
-
attrs.each { |attr_group| xml.instruct!(target, attr_group) }
-
end
-
end
-
end
-
-
feed_opts = {"xml:lang" => options[:language] || "en-US", "xmlns" => 'http://www.w3.org/2005/Atom'}
-
feed_opts.merge!(options).reject!{|k,v| !k.to_s.match(/^xml/)}
-
-
xml.feed(feed_opts) do
-
xml.id(options[:id] || "tag:#{request.host},#{options[:schema_date]}:#{request.fullpath.split(".")[0]}")
-
xml.link(:rel => 'alternate', :type => 'text/html', :href => options[:root_url] || (request.protocol + request.host_with_port))
-
xml.link(:rel => 'self', :type => 'application/atom+xml', :href => options[:url] || request.url)
-
-
yield AtomFeedBuilder.new(xml, self, options)
-
end
-
end
-
-
1
class AtomBuilder
-
1
XHTML_TAG_NAMES = %w(content rights title subtitle summary).to_set
-
-
1
def initialize(xml)
-
@xml = xml
-
end
-
-
1
private
-
# Delegate to xml builder, first wrapping the element in a xhtml
-
# namespaced div element if the method and arguments indicate
-
# that an xhtml_block? is desired.
-
1
def method_missing(method, *arguments, &block)
-
if xhtml_block?(method, arguments)
-
@xml.__send__(method, *arguments) do
-
@xml.div(:xmlns => 'http://www.w3.org/1999/xhtml') do |xhtml|
-
block.call(xhtml)
-
end
-
end
-
else
-
@xml.__send__(method, *arguments, &block)
-
end
-
end
-
-
# True if the method name matches one of the five elements defined
-
# in the Atom spec as potentially containing XHTML content and
-
# if type: 'xhtml' is, in fact, specified.
-
1
def xhtml_block?(method, arguments)
-
if XHTML_TAG_NAMES.include?(method.to_s)
-
last = arguments.last
-
last.is_a?(Hash) && last[:type].to_s == 'xhtml'
-
end
-
end
-
end
-
-
1
class AtomFeedBuilder < AtomBuilder
-
1
def initialize(xml, view, feed_options = {})
-
@xml, @view, @feed_options = xml, view, feed_options
-
end
-
-
# Accepts a Date or Time object and inserts it in the proper format. If nil is passed, current time in UTC is used.
-
1
def updated(date_or_time = nil)
-
@xml.updated((date_or_time || Time.now.utc).xmlschema)
-
end
-
-
# Creates an entry tag for a specific record and prefills the id using class and id.
-
#
-
# Options:
-
#
-
# * <tt>:published</tt>: Time first published. Defaults to the created_at attribute on the record if one such exists.
-
# * <tt>:updated</tt>: Time of update. Defaults to the updated_at attribute on the record if one such exists.
-
# * <tt>:url</tt>: The URL for this entry. Defaults to the polymorphic_url for the record.
-
# * <tt>:id</tt>: The ID for this entry. Defaults to "tag:#{@view.request.host},#{@feed_options[:schema_date]}:#{record.class}/#{record.id}"
-
# * <tt>:type</tt>: The TYPE for this entry. Defaults to "text/html".
-
1
def entry(record, options = {})
-
@xml.entry do
-
@xml.id(options[:id] || "tag:#{@view.request.host},#{@feed_options[:schema_date]}:#{record.class}/#{record.id}")
-
-
if options[:published] || (record.respond_to?(:created_at) && record.created_at)
-
@xml.published((options[:published] || record.created_at).xmlschema)
-
end
-
-
if options[:updated] || (record.respond_to?(:updated_at) && record.updated_at)
-
@xml.updated((options[:updated] || record.updated_at).xmlschema)
-
end
-
-
type = options.fetch(:type, 'text/html')
-
-
@xml.link(:rel => 'alternate', :type => type, :href => options[:url] || @view.polymorphic_url(record))
-
-
yield AtomBuilder.new(@xml)
-
end
-
end
-
end
-
-
end
-
end
-
end
-
1
require 'active_support/benchmarkable'
-
-
1
module ActionView
-
1
module Helpers
-
1
module BenchmarkHelper
-
1
include ActiveSupport::Benchmarkable
-
-
1
def benchmark(*)
-
capture { super }
-
end
-
end
-
end
-
end
-
1
module ActionView
-
# = Action View Cache Helper
-
1
module Helpers
-
1
module CacheHelper
-
# This helper exposes a method for caching fragments of a view
-
# rather than an entire action or page. This technique is useful
-
# caching pieces like menus, lists of newstopics, static HTML
-
# fragments, and so on. This method takes a block that contains
-
# the content you wish to cache.
-
#
-
# The best way to use this is by doing key-based cache expiration
-
# on top of a cache store like Memcached that'll automatically
-
# kick out old entries. For more on key-based expiration, see:
-
# http://37signals.com/svn/posts/3113-how-key-based-cache-expiration-works
-
#
-
# When using this method, you list the cache dependency as the name of the cache, like so:
-
#
-
# <% cache project do %>
-
# <b>All the topics on this project</b>
-
# <%= render project.topics %>
-
# <% end %>
-
#
-
# This approach will assume that when a new topic is added, you'll touch
-
# the project. The cache key generated from this call will be something like:
-
#
-
# views/projects/123-20120806214154/7a1156131a6928cb0026877f8b749ac9
-
# ^class ^id ^updated_at ^template tree digest
-
#
-
# The cache is thus automatically bumped whenever the project updated_at is touched.
-
#
-
# If your template cache depends on multiple sources (try to avoid this to keep things simple),
-
# you can name all these dependencies as part of an array:
-
#
-
# <% cache [ project, current_user ] do %>
-
# <b>All the topics on this project</b>
-
# <%= render project.topics %>
-
# <% end %>
-
#
-
# This will include both records as part of the cache key and updating either of them will
-
# expire the cache.
-
#
-
# ==== Template digest
-
#
-
# The template digest that's added to the cache key is computed by taking an md5 of the
-
# contents of the entire template file. This ensures that your caches will automatically
-
# expire when you change the template file.
-
#
-
# Note that the md5 is taken of the entire template file, not just what's within the
-
# cache do/end call. So it's possible that changing something outside of that call will
-
# still expire the cache.
-
#
-
# Additionally, the digestor will automatically look through your template file for
-
# explicit and implicit dependencies, and include those as part of the digest.
-
#
-
# ==== Implicit dependencies
-
#
-
# Most template dependencies can be derived from calls to render in the template itself.
-
# Here are some examples of render calls that Cache Digests knows how to decode:
-
#
-
# render partial: "comments/comment", collection: commentable.comments
-
# render "comments/comments"
-
# render 'comments/comments'
-
# render('comments/comments')
-
#
-
# render "header" => render("comments/header")
-
#
-
# render(@topic) => render("topics/topic")
-
# render(topics) => render("topics/topic")
-
# render(message.topics) => render("topics/topic")
-
#
-
# It's not possible to derive all render calls like that, though. Here are a few examples of things that can't be derived:
-
#
-
# render group_of_attachments
-
# render @project.documents.where(published: true).order('created_at')
-
#
-
# You will have to rewrite those to the explicit form:
-
#
-
# render partial: 'attachments/attachment', collection: group_of_attachments
-
# render partial: 'documents/document', collection: @project.documents.where(published: true).order('created_at')
-
#
-
# === Explicit dependencies
-
#
-
# Some times you'll have template dependencies that can't be derived at all. This is typically
-
# the case when you have template rendering that happens in helpers. Here's an example:
-
#
-
# <%= render_sortable_todolists @project.todolists %>
-
#
-
# You'll need to use a special comment format to call those out:
-
#
-
# <%# Template Dependency: todolists/todolist %>
-
# <%= render_sortable_todolists @project.todolists %>
-
#
-
# The pattern used to match these is /# Template Dependency: ([^ ]+)/, so it's important that you type it out just so.
-
# You can only declare one template dependency per line.
-
#
-
# === External dependencies
-
#
-
# If you use a helper method, for example, inside of a cached block and you then update that helper,
-
# you'll have to bump the cache as well. It doesn't really matter how you do it, but the md5 of the template file
-
# must change. One recommendation is to simply be explicit in a comment, like:
-
#
-
# <%# Helper Dependency Updated: May 6, 2012 at 6pm %>
-
# <%= some_helper_method(person) %>
-
#
-
# Now all you'll have to do is change that timestamp when the helper method changes.
-
1
def cache(name = {}, options = nil, &block)
-
if controller.perform_caching
-
safe_concat(fragment_for(fragment_name_with_digest(name), options, &block))
-
else
-
yield
-
end
-
-
nil
-
end
-
-
1
def fragment_name_with_digest(name) #:nodoc:
-
if @virtual_path
-
[
-
*Array(name.is_a?(Hash) ? controller.url_for(name).split("://").last : name),
-
Digestor.digest(@virtual_path, formats.last.to_sym, lookup_context)
-
]
-
else
-
name
-
end
-
end
-
-
1
private
-
# TODO: Create an object that has caching read/write on it
-
1
def fragment_for(name = {}, options = nil, &block) #:nodoc:
-
if fragment = controller.read_fragment(name, options)
-
fragment
-
else
-
# VIEW TODO: Make #capture usable outside of ERB
-
# This dance is needed because Builder can't use capture
-
pos = output_buffer.length
-
yield
-
output_safe = output_buffer.html_safe?
-
fragment = output_buffer.slice!(pos..-1)
-
if output_safe
-
self.output_buffer = output_buffer.class.new(output_buffer)
-
end
-
controller.write_fragment(name, fragment, options)
-
end
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/string/output_safety'
-
-
1
module ActionView
-
# = Action View Capture Helper
-
1
module Helpers
-
# CaptureHelper exposes methods to let you extract generated markup which
-
# can be used in other parts of a template or layout file.
-
#
-
# It provides a method to capture blocks into variables through capture and
-
# a way to capture a block of markup for use in a layout through content_for.
-
1
module CaptureHelper
-
# The capture method allows you to extract part of a template into a
-
# variable. You can then use this variable anywhere in your templates or layout.
-
#
-
# The capture method can be used in ERB templates...
-
#
-
# <% @greeting = capture do %>
-
# Welcome to my shiny new web page! The date and time is
-
# <%= Time.now %>
-
# <% end %>
-
#
-
# ...and Builder (RXML) templates.
-
#
-
# @timestamp = capture do
-
# "The current timestamp is #{Time.now}."
-
# end
-
#
-
# You can then use that variable anywhere else. For example:
-
#
-
# <html>
-
# <head><title><%= @greeting %></title></head>
-
# <body>
-
# <b><%= @greeting %></b>
-
# </body></html>
-
#
-
1
def capture(*args)
-
value = nil
-
buffer = with_output_buffer { value = yield(*args) }
-
if string = buffer.presence || value and string.is_a?(String)
-
ERB::Util.html_escape string
-
end
-
end
-
-
# Calling content_for stores a block of markup in an identifier for later use.
-
# You can make subsequent calls to the stored content in other templates, helper modules
-
# or the layout by passing the identifier as an argument to <tt>content_for</tt>.
-
#
-
# Note: <tt>yield</tt> can still be used to retrieve the stored content, but calling
-
# <tt>yield</tt> doesn't work in helper modules, while <tt>content_for</tt> does.
-
#
-
# ==== Examples
-
#
-
# <% content_for :not_authorized do %>
-
# alert('You are not authorized to do that!')
-
# <% end %>
-
#
-
# You can then use <tt>content_for :not_authorized</tt> anywhere in your templates.
-
#
-
# <%= content_for :not_authorized if current_user.nil? %>
-
#
-
# This is equivalent to:
-
#
-
# <%= yield :not_authorized if current_user.nil? %>
-
#
-
# <tt>content_for</tt>, however, can also be used in helper modules.
-
#
-
# module StorageHelper
-
# def stored_content
-
# content_for(:storage) || "Your storage is empty"
-
# end
-
# end
-
#
-
# This helper works just like normal helpers.
-
#
-
# <%= stored_content %>
-
#
-
# You can use the <tt>yield</tt> syntax alongside an existing call to <tt>yield</tt> in a layout. For example:
-
#
-
# <%# This is the layout %>
-
# <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
-
# <head>
-
# <title>My Website</title>
-
# <%= yield :script %>
-
# </head>
-
# <body>
-
# <%= yield %>
-
# </body>
-
# </html>
-
#
-
# And now, we'll create a view that has a <tt>content_for</tt> call that
-
# creates the <tt>script</tt> identifier.
-
#
-
# <%# This is our view %>
-
# Please login!
-
#
-
# <% content_for :script do %>
-
# <script>alert('You are not authorized to view this page!')</script>
-
# <% end %>
-
#
-
# Then, in another view, you could to do something like this:
-
#
-
# <%= link_to 'Logout', action: 'logout', remote: true %>
-
#
-
# <% content_for :script do %>
-
# <%= javascript_include_tag :defaults %>
-
# <% end %>
-
#
-
# That will place +script+ tags for your default set of JavaScript files on the page;
-
# this technique is useful if you'll only be using these scripts in a few views.
-
#
-
# Note that content_for concatenates (default) the blocks it is given for a particular
-
# identifier in order. For example:
-
#
-
# <% content_for :navigation do %>
-
# <li><%= link_to 'Home', action: 'index' %></li>
-
# <% end %>
-
#
-
# <%# Add some other content, or use a different template: %>
-
#
-
# <% content_for :navigation do %>
-
# <li><%= link_to 'Login', action: 'login' %></li>
-
# <% end %>
-
#
-
# Then, in another template or layout, this code would render both links in order:
-
#
-
# <ul><%= content_for :navigation %></ul>
-
#
-
# If the flush parameter is true content_for replaces the blocks it is given. For example:
-
#
-
# <% content_for :navigation do %>
-
# <li><%= link_to 'Home', action: 'index' %></li>
-
# <% end %>
-
#
-
# <%# Add some other content, or use a different template: %>
-
#
-
# <% content_for :navigation, flush: true do %>
-
# <li><%= link_to 'Login', action: 'login' %></li>
-
# <% end %>
-
#
-
# Then, in another template or layout, this code would render only the last link:
-
#
-
# <ul><%= content_for :navigation %></ul>
-
#
-
# Lastly, simple content can be passed as a parameter:
-
#
-
# <% content_for :script, javascript_include_tag(:defaults) %>
-
#
-
# WARNING: content_for is ignored in caches. So you shouldn't use it
-
# for elements that will be fragment cached.
-
1
def content_for(name, content = nil, options = {}, &block)
-
if content || block_given?
-
if block_given?
-
options = content if content
-
content = capture(&block)
-
end
-
if content
-
options[:flush] ? @view_flow.set(name, content) : @view_flow.append(name, content)
-
end
-
nil
-
else
-
@view_flow.get(name)
-
end
-
end
-
-
# The same as +content_for+ but when used with streaming flushes
-
# straight back to the layout. In other words, if you want to
-
# concatenate several times to the same buffer when rendering a given
-
# template, you should use +content_for+, if not, use +provide+ to tell
-
# the layout to stop looking for more contents.
-
1
def provide(name, content = nil, &block)
-
content = capture(&block) if block_given?
-
result = @view_flow.append!(name, content) if content
-
result unless content
-
end
-
-
# content_for? simply checks whether any content has been captured yet using content_for
-
# Useful to render parts of your layout differently based on what is in your views.
-
#
-
# ==== Examples
-
#
-
# Perhaps you will use different css in you layout if no content_for :right_column
-
#
-
# <%# This is the layout %>
-
# <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
-
# <head>
-
# <title>My Website</title>
-
# <%= yield :script %>
-
# </head>
-
# <body class="<%= content_for?(:right_col) ? 'one-column' : 'two-column' %>">
-
# <%= yield %>
-
# <%= yield :right_col %>
-
# </body>
-
# </html>
-
1
def content_for?(name)
-
@view_flow.get(name).present?
-
end
-
-
# Use an alternate output buffer for the duration of the block.
-
# Defaults to a new empty string.
-
1
def with_output_buffer(buf = nil) #:nodoc:
-
unless buf
-
buf = ActionView::OutputBuffer.new
-
buf.force_encoding(output_buffer.encoding) if output_buffer
-
end
-
self.output_buffer, old_buffer = buf, output_buffer
-
yield
-
output_buffer
-
ensure
-
self.output_buffer = old_buffer
-
end
-
-
# Add the output buffer to the response body and start a new one.
-
1
def flush_output_buffer #:nodoc:
-
if output_buffer && !output_buffer.empty?
-
response.stream.write output_buffer
-
self.output_buffer = output_buffer.respond_to?(:clone_empty) ? output_buffer.clone_empty : output_buffer[0, 0]
-
nil
-
end
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/module/attr_internal'
-
-
1
module ActionView
-
1
module Helpers
-
# This module keeps all methods and behavior in ActionView
-
# that simply delegates to the controller.
-
1
module ControllerHelper #:nodoc:
-
1
attr_internal :controller, :request
-
-
1
delegate :request_forgery_protection_token, :params, :session, :cookies, :response, :headers,
-
:flash, :action_name, :controller_name, :controller_path, :to => :controller
-
-
1
def assign_controller(controller)
-
137
if @_controller = controller
-
137
@_request = controller.request if controller.respond_to?(:request)
-
137
@_config = controller.config.inheritable_copy if controller.respond_to?(:config)
-
end
-
end
-
-
1
def logger
-
controller.logger if controller.respond_to?(:logger)
-
end
-
end
-
end
-
end
-
1
module ActionView
-
# = Action View CSRF Helper
-
1
module Helpers
-
1
module CsrfHelper
-
# Returns meta tags "csrf-param" and "csrf-token" with the name of the cross-site
-
# request forgery protection parameter and token, respectively.
-
#
-
# <head>
-
# <%= csrf_meta_tags %>
-
# </head>
-
#
-
# These are used to generate the dynamic forms that implement non-remote links with
-
# <tt>:method</tt>.
-
#
-
# Note that regular forms generate hidden fields, and that Ajax calls are whitelisted,
-
# so they do not use these tags.
-
1
def csrf_meta_tags
-
if protect_against_forgery?
-
[
-
tag('meta', :name => 'csrf-param', :content => request_forgery_protection_token),
-
tag('meta', :name => 'csrf-token', :content => form_authenticity_token)
-
].join("\n").html_safe
-
end
-
end
-
-
# For backwards compatibility.
-
1
alias csrf_meta_tag csrf_meta_tags
-
end
-
end
-
end
-
1
require 'date'
-
1
require 'action_view/helpers/tag_helper'
-
1
require 'active_support/core_ext/date/conversions'
-
1
require 'active_support/core_ext/hash/slice'
-
1
require 'active_support/core_ext/object/with_options'
-
-
1
module ActionView
-
1
module Helpers
-
# = Action View Date Helpers
-
#
-
# The Date Helper primarily creates select/option tags for different kinds of dates and times or date and time
-
# elements. All of the select-type methods share a number of common options that are as follows:
-
#
-
# * <tt>:prefix</tt> - overwrites the default prefix of "date" used for the select names. So specifying "birthday"
-
# would give \birthday[month] instead of \date[month] if passed to the <tt>select_month</tt> method.
-
# * <tt>:include_blank</tt> - set to true if it should be possible to set an empty date.
-
# * <tt>:discard_type</tt> - set to true if you want to discard the type part of the select name. If set to true,
-
# the <tt>select_month</tt> method would use simply "date" (which can be overwritten using <tt>:prefix</tt>) instead
-
# of \date[month].
-
1
module DateHelper
-
# Reports the approximate distance in time between two Time, Date or DateTime objects or integers as seconds.
-
# Pass <tt>include_seconds: true</tt> if you want more detailed approximations when distance < 1 min, 29 secs.
-
# Distances are reported based on the following table:
-
#
-
# 0 <-> 29 secs # => less than a minute
-
# 30 secs <-> 1 min, 29 secs # => 1 minute
-
# 1 min, 30 secs <-> 44 mins, 29 secs # => [2..44] minutes
-
# 44 mins, 30 secs <-> 89 mins, 29 secs # => about 1 hour
-
# 89 mins, 30 secs <-> 23 hrs, 59 mins, 29 secs # => about [2..24] hours
-
# 23 hrs, 59 mins, 30 secs <-> 41 hrs, 59 mins, 29 secs # => 1 day
-
# 41 hrs, 59 mins, 30 secs <-> 29 days, 23 hrs, 59 mins, 29 secs # => [2..29] days
-
# 29 days, 23 hrs, 59 mins, 30 secs <-> 44 days, 23 hrs, 59 mins, 29 secs # => about 1 month
-
# 44 days, 23 hrs, 59 mins, 30 secs <-> 59 days, 23 hrs, 59 mins, 29 secs # => about 2 months
-
# 59 days, 23 hrs, 59 mins, 30 secs <-> 1 yr minus 1 sec # => [2..12] months
-
# 1 yr <-> 1 yr, 3 months # => about 1 year
-
# 1 yr, 3 months <-> 1 yr, 9 months # => over 1 year
-
# 1 yr, 9 months <-> 2 yr minus 1 sec # => almost 2 years
-
# 2 yrs <-> max time or date # => (same rules as 1 yr)
-
#
-
# With <tt>include_seconds: true</tt> and the difference < 1 minute 29 seconds:
-
# 0-4 secs # => less than 5 seconds
-
# 5-9 secs # => less than 10 seconds
-
# 10-19 secs # => less than 20 seconds
-
# 20-39 secs # => half a minute
-
# 40-59 secs # => less than a minute
-
# 60-89 secs # => 1 minute
-
#
-
# ==== Examples
-
# from_time = Time.now
-
# distance_of_time_in_words(from_time, from_time + 50.minutes) # => about 1 hour
-
# distance_of_time_in_words(from_time, 50.minutes.from_now) # => about 1 hour
-
# distance_of_time_in_words(from_time, from_time + 15.seconds) # => less than a minute
-
# distance_of_time_in_words(from_time, from_time + 15.seconds, include_seconds: true) # => less than 20 seconds
-
# distance_of_time_in_words(from_time, 3.years.from_now) # => about 3 years
-
# distance_of_time_in_words(from_time, from_time + 60.hours) # => 3 days
-
# distance_of_time_in_words(from_time, from_time + 45.seconds, include_seconds: true) # => less than a minute
-
# distance_of_time_in_words(from_time, from_time - 45.seconds, include_seconds: true) # => less than a minute
-
# distance_of_time_in_words(from_time, 76.seconds.from_now) # => 1 minute
-
# distance_of_time_in_words(from_time, from_time + 1.year + 3.days) # => about 1 year
-
# distance_of_time_in_words(from_time, from_time + 3.years + 6.months) # => over 3 years
-
# distance_of_time_in_words(from_time, from_time + 4.years + 9.days + 30.minutes + 5.seconds) # => about 4 years
-
#
-
# to_time = Time.now + 6.years + 19.days
-
# distance_of_time_in_words(from_time, to_time, include_seconds: true) # => about 6 years
-
# distance_of_time_in_words(to_time, from_time, include_seconds: true) # => about 6 years
-
# distance_of_time_in_words(Time.now, Time.now) # => less than a minute
-
1
def distance_of_time_in_words(from_time, to_time = 0, include_seconds_or_options = {}, options = {})
-
if include_seconds_or_options.is_a?(Hash)
-
options = include_seconds_or_options
-
else
-
ActiveSupport::Deprecation.warn "distance_of_time_in_words and time_ago_in_words now accept :include_seconds " +
-
"as a part of options hash, not a boolean argument"
-
options[:include_seconds] ||= !!include_seconds_or_options
-
end
-
-
options = {
-
scope: :'datetime.distance_in_words'
-
}.merge!(options)
-
-
from_time = from_time.to_time if from_time.respond_to?(:to_time)
-
to_time = to_time.to_time if to_time.respond_to?(:to_time)
-
from_time, to_time = to_time, from_time if from_time > to_time
-
distance_in_minutes = ((to_time - from_time)/60.0).round
-
distance_in_seconds = (to_time - from_time).round
-
-
I18n.with_options :locale => options[:locale], :scope => options[:scope] do |locale|
-
case distance_in_minutes
-
when 0..1
-
return distance_in_minutes == 0 ?
-
locale.t(:less_than_x_minutes, :count => 1) :
-
locale.t(:x_minutes, :count => distance_in_minutes) unless options[:include_seconds]
-
-
case distance_in_seconds
-
when 0..4 then locale.t :less_than_x_seconds, :count => 5
-
when 5..9 then locale.t :less_than_x_seconds, :count => 10
-
when 10..19 then locale.t :less_than_x_seconds, :count => 20
-
when 20..39 then locale.t :half_a_minute
-
when 40..59 then locale.t :less_than_x_minutes, :count => 1
-
else locale.t :x_minutes, :count => 1
-
end
-
-
when 2...45 then locale.t :x_minutes, :count => distance_in_minutes
-
when 45...90 then locale.t :about_x_hours, :count => 1
-
# 90 mins up to 24 hours
-
when 90...1440 then locale.t :about_x_hours, :count => (distance_in_minutes.to_f / 60.0).round
-
# 24 hours up to 42 hours
-
when 1440...2520 then locale.t :x_days, :count => 1
-
# 42 hours up to 30 days
-
when 2520...43200 then locale.t :x_days, :count => (distance_in_minutes.to_f / 1440.0).round
-
# 30 days up to 60 days
-
when 43200...86400 then locale.t :about_x_months, :count => (distance_in_minutes.to_f / 43200.0).round
-
# 60 days up to 365 days
-
when 86400...525600 then locale.t :x_months, :count => (distance_in_minutes.to_f / 43200.0).round
-
else
-
if from_time.acts_like?(:time) && to_time.acts_like?(:time)
-
fyear = from_time.year
-
fyear += 1 if from_time.month >= 3
-
tyear = to_time.year
-
tyear -= 1 if to_time.month < 3
-
leap_years = (fyear > tyear) ? 0 : (fyear..tyear).count{|x| Date.leap?(x)}
-
minute_offset_for_leap_year = leap_years * 1440
-
# Discount the leap year days when calculating year distance.
-
# e.g. if there are 20 leap year days between 2 dates having the same day
-
# and month then the based on 365 days calculation
-
# the distance in years will come out to over 80 years when in written
-
# english it would read better as about 80 years.
-
minutes_with_offset = distance_in_minutes - minute_offset_for_leap_year
-
else
-
minutes_with_offset = distance_in_minutes
-
end
-
remainder = (minutes_with_offset % 525600)
-
distance_in_years = (minutes_with_offset / 525600)
-
if remainder < 131400
-
locale.t(:about_x_years, :count => distance_in_years)
-
elsif remainder < 394200
-
locale.t(:over_x_years, :count => distance_in_years)
-
else
-
locale.t(:almost_x_years, :count => distance_in_years + 1)
-
end
-
end
-
end
-
end
-
-
# Like <tt>distance_of_time_in_words</tt>, but where <tt>to_time</tt> is fixed to <tt>Time.now</tt>.
-
#
-
# time_ago_in_words(3.minutes.from_now) # => 3 minutes
-
# time_ago_in_words(3.minutes.ago) # => 3 minutes
-
# time_ago_in_words(Time.now - 15.hours) # => about 15 hours
-
# time_ago_in_words(Time.now) # => less than a minute
-
# time_ago_in_words(Time.now, include_seconds: true) # => less than 5 seconds
-
#
-
# from_time = Time.now - 3.days - 14.minutes - 25.seconds
-
# time_ago_in_words(from_time) # => 3 days
-
#
-
# from_time = (3.days + 14.minutes + 25.seconds).ago
-
# time_ago_in_words(from_time) # => 3 days
-
#
-
# Note that you cannot pass a <tt>Numeric</tt> value to <tt>time_ago_in_words</tt>.
-
#
-
1
def time_ago_in_words(from_time, include_seconds_or_options = {})
-
distance_of_time_in_words(from_time, Time.now, include_seconds_or_options)
-
end
-
-
1
alias_method :distance_of_time_in_words_to_now, :time_ago_in_words
-
-
# Returns a set of select tags (one for year, month, and day) pre-selected for accessing a specified date-based
-
# attribute (identified by +method+) on an object assigned to the template (identified by +object+).
-
#
-
#
-
# ==== Options
-
# * <tt>:use_month_numbers</tt> - Set to true if you want to use month numbers rather than month names (e.g.
-
# "2" instead of "February").
-
# * <tt>:use_two_digit_numbers</tt> - Set to true if you want to display two digit month and day numbers (e.g.
-
# "02" instead of "February" and "08" instead of "8").
-
# * <tt>:use_short_month</tt> - Set to true if you want to use abbreviated month names instead of full
-
# month names (e.g. "Feb" instead of "February").
-
# * <tt>:add_month_numbers</tt> - Set to true if you want to use both month numbers and month names (e.g.
-
# "2 - February" instead of "February").
-
# * <tt>:use_month_names</tt> - Set to an array with 12 month names if you want to customize month names.
-
# Note: You can also use Rails' i18n functionality for this.
-
# * <tt>:date_separator</tt> - Specifies a string to separate the date fields. Default is "" (i.e. nothing).
-
# * <tt>:start_year</tt> - Set the start year for the year select. Default is <tt>Time.now.year - 5</tt>.
-
# * <tt>:end_year</tt> - Set the end year for the year select. Default is <tt>Time.now.year + 5</tt>.
-
# * <tt>:discard_day</tt> - Set to true if you don't want to show a day select. This includes the day
-
# as a hidden field instead of showing a select field. Also note that this implicitly sets the day to be the
-
# first of the given month in order to not create invalid dates like 31 February.
-
# * <tt>:discard_month</tt> - Set to true if you don't want to show a month select. This includes the month
-
# as a hidden field instead of showing a select field. Also note that this implicitly sets :discard_day to true.
-
# * <tt>:discard_year</tt> - Set to true if you don't want to show a year select. This includes the year
-
# as a hidden field instead of showing a select field.
-
# * <tt>:order</tt> - Set to an array containing <tt>:day</tt>, <tt>:month</tt> and <tt>:year</tt> to
-
# customize the order in which the select fields are shown. If you leave out any of the symbols, the respective
-
# select will not be shown (like when you set <tt>discard_xxx: true</tt>. Defaults to the order defined in
-
# the respective locale (e.g. [:year, :month, :day] in the en locale that ships with Rails).
-
# * <tt>:include_blank</tt> - Include a blank option in every select field so it's possible to set empty
-
# dates.
-
# * <tt>:default</tt> - Set a default date if the affected date isn't set or is nil.
-
# * <tt>:disabled</tt> - Set to true if you want show the select fields as disabled.
-
# * <tt>:prompt</tt> - Set to true (for a generic prompt), a prompt string or a hash of prompt strings
-
# for <tt>:year</tt>, <tt>:month</tt>, <tt>:day</tt>, <tt>:hour</tt>, <tt>:minute</tt> and <tt>:second</tt>.
-
# Setting this option prepends a select option with a generic prompt (Day, Month, Year, Hour, Minute, Seconds)
-
# or the given prompt string.
-
# * <tt>:with_css_classes</tt> - Set to true if you want assign different styles for 'select' tags. This option
-
# automatically set classes 'year', 'month', 'day', 'hour', 'minute' and 'second' for your 'select' tags.
-
#
-
# If anything is passed in the +html_options+ hash it will be applied to every select tag in the set.
-
#
-
# NOTE: Discarded selects will default to 1. So if no month select is available, January will be assumed.
-
#
-
# # Generates a date select that when POSTed is stored in the article variable, in the written_on attribute.
-
# date_select("article", "written_on")
-
#
-
# # Generates a date select that when POSTed is stored in the article variable, in the written_on attribute,
-
# # with the year in the year drop down box starting at 1995.
-
# date_select("article", "written_on", start_year: 1995)
-
#
-
# # Generates a date select that when POSTed is stored in the article variable, in the written_on attribute,
-
# # with the year in the year drop down box starting at 1995, numbers used for months instead of words,
-
# # and without a day select box.
-
# date_select("article", "written_on", start_year: 1995, use_month_numbers: true,
-
# discard_day: true, include_blank: true)
-
#
-
# # Generates a date select that when POSTed is stored in the article variable, in the written_on attribute,
-
# # with two digit numbers used for months and days.
-
# date_select("article", "written_on", use_two_digit_numbers: true)
-
#
-
# # Generates a date select that when POSTed is stored in the article variable, in the written_on attribute
-
# # with the fields ordered as day, month, year rather than month, day, year.
-
# date_select("article", "written_on", order: [:day, :month, :year])
-
#
-
# # Generates a date select that when POSTed is stored in the user variable, in the birthday attribute
-
# # lacking a year field.
-
# date_select("user", "birthday", order: [:month, :day])
-
#
-
# # Generates a date select that when POSTed is stored in the article variable, in the written_on attribute
-
# # which is initially set to the date 3 days from the current date
-
# date_select("article", "written_on", default: 3.days.from_now)
-
#
-
# # Generates a date select that when POSTed is stored in the credit_card variable, in the bill_due attribute
-
# # that will have a default day of 20.
-
# date_select("credit_card", "bill_due", default: { day: 20 })
-
#
-
# # Generates a date select with custom prompts.
-
# date_select("article", "written_on", prompt: { day: 'Select day', month: 'Select month', year: 'Select year' })
-
#
-
# The selects are prepared for multi-parameter assignment to an Active Record object.
-
#
-
# Note: If the day is not included as an option but the month is, the day will be set to the 1st to ensure that
-
# all month choices are valid.
-
1
def date_select(object_name, method, options = {}, html_options = {})
-
Tags::DateSelect.new(object_name, method, self, options, html_options).render
-
end
-
-
# Returns a set of select tags (one for hour, minute and optionally second) pre-selected for accessing a
-
# specified time-based attribute (identified by +method+) on an object assigned to the template (identified by
-
# +object+). You can include the seconds with <tt>:include_seconds</tt>. You can get hours in the AM/PM format
-
# with <tt>:ampm</tt> option.
-
#
-
# This method will also generate 3 input hidden tags, for the actual year, month and day unless the option
-
# <tt>:ignore_date</tt> is set to +true+. If you set the <tt>:ignore_date</tt> to +true+, you must have a
-
# +date_select+ on the same method within the form otherwise an exception will be raised.
-
#
-
# If anything is passed in the html_options hash it will be applied to every select tag in the set.
-
#
-
# # Creates a time select tag that, when POSTed, will be stored in the article variable in the sunrise attribute.
-
# time_select("article", "sunrise")
-
#
-
# # Creates a time select tag with a seconds field that, when POSTed, will be stored in the article variables in
-
# # the sunrise attribute.
-
# time_select("article", "start_time", include_seconds: true)
-
#
-
# # You can set the <tt>:minute_step</tt> to 15 which will give you: 00, 15, 30 and 45.
-
# time_select 'game', 'game_time', {minute_step: 15}
-
#
-
# # Creates a time select tag with a custom prompt. Use <tt>prompt: true</tt> for generic prompts.
-
# time_select("article", "written_on", prompt: {hour: 'Choose hour', minute: 'Choose minute', second: 'Choose seconds'})
-
# time_select("article", "written_on", prompt: {hour: true}) # generic prompt for hours
-
# time_select("article", "written_on", prompt: true) # generic prompts for all
-
#
-
# # You can set :ampm option to true which will show the hours as: 12 PM, 01 AM .. 11 PM.
-
# time_select 'game', 'game_time', {ampm: true}
-
#
-
# The selects are prepared for multi-parameter assignment to an Active Record object.
-
#
-
# Note: If the day is not included as an option but the month is, the day will be set to the 1st to ensure that
-
# all month choices are valid.
-
1
def time_select(object_name, method, options = {}, html_options = {})
-
Tags::TimeSelect.new(object_name, method, self, options, html_options).render
-
end
-
-
# Returns a set of select tags (one for year, month, day, hour, and minute) pre-selected for accessing a
-
# specified datetime-based attribute (identified by +method+) on an object assigned to the template (identified
-
# by +object+).
-
#
-
# If anything is passed in the html_options hash it will be applied to every select tag in the set.
-
#
-
# # Generates a datetime select that, when POSTed, will be stored in the article variable in the written_on
-
# # attribute.
-
# datetime_select("article", "written_on")
-
#
-
# # Generates a datetime select with a year select that starts at 1995 that, when POSTed, will be stored in the
-
# # article variable in the written_on attribute.
-
# datetime_select("article", "written_on", start_year: 1995)
-
#
-
# # Generates a datetime select with a default value of 3 days from the current time that, when POSTed, will
-
# # be stored in the trip variable in the departing attribute.
-
# datetime_select("trip", "departing", default: 3.days.from_now)
-
#
-
# # Generate a datetime select with hours in the AM/PM format
-
# datetime_select("article", "written_on", ampm: true)
-
#
-
# # Generates a datetime select that discards the type that, when POSTed, will be stored in the article variable
-
# # as the written_on attribute.
-
# datetime_select("article", "written_on", discard_type: true)
-
#
-
# # Generates a datetime select with a custom prompt. Use <tt>prompt: true</tt> for generic prompts.
-
# datetime_select("article", "written_on", prompt: {day: 'Choose day', month: 'Choose month', year: 'Choose year'})
-
# datetime_select("article", "written_on", prompt: {hour: true}) # generic prompt for hours
-
# datetime_select("article", "written_on", prompt: true) # generic prompts for all
-
#
-
# The selects are prepared for multi-parameter assignment to an Active Record object.
-
1
def datetime_select(object_name, method, options = {}, html_options = {})
-
Tags::DatetimeSelect.new(object_name, method, self, options, html_options).render
-
end
-
-
# Returns a set of html select-tags (one for year, month, day, hour, minute, and second) pre-selected with the
-
# +datetime+. It's also possible to explicitly set the order of the tags using the <tt>:order</tt> option with
-
# an array of symbols <tt>:year</tt>, <tt>:month</tt> and <tt>:day</tt> in the desired order. If you do not
-
# supply a Symbol, it will be appended onto the <tt>:order</tt> passed in. You can also add
-
# <tt>:date_separator</tt>, <tt>:datetime_separator</tt> and <tt>:time_separator</tt> keys to the +options+ to
-
# control visual display of the elements.
-
#
-
# If anything is passed in the html_options hash it will be applied to every select tag in the set.
-
#
-
# my_date_time = Time.now + 4.days
-
#
-
# # Generates a datetime select that defaults to the datetime in my_date_time (four days after today).
-
# select_datetime(my_date_time)
-
#
-
# # Generates a datetime select that defaults to today (no specified datetime)
-
# select_datetime()
-
#
-
# # Generates a datetime select that defaults to the datetime in my_date_time (four days after today)
-
# # with the fields ordered year, month, day rather than month, day, year.
-
# select_datetime(my_date_time, order: [:year, :month, :day])
-
#
-
# # Generates a datetime select that defaults to the datetime in my_date_time (four days after today)
-
# # with a '/' between each date field.
-
# select_datetime(my_date_time, date_separator: '/')
-
#
-
# # Generates a datetime select that defaults to the datetime in my_date_time (four days after today)
-
# # with a date fields separated by '/', time fields separated by '' and the date and time fields
-
# # separated by a comma (',').
-
# select_datetime(my_date_time, date_separator: '/', time_separator: '', datetime_separator: ',')
-
#
-
# # Generates a datetime select that discards the type of the field and defaults to the datetime in
-
# # my_date_time (four days after today)
-
# select_datetime(my_date_time, discard_type: true)
-
#
-
# # Generate a datetime field with hours in the AM/PM format
-
# select_datetime(my_date_time, ampm: true)
-
#
-
# # Generates a datetime select that defaults to the datetime in my_date_time (four days after today)
-
# # prefixed with 'payday' rather than 'date'
-
# select_datetime(my_date_time, prefix: 'payday')
-
#
-
# # Generates a datetime select with a custom prompt. Use <tt>prompt: true</tt> for generic prompts.
-
# select_datetime(my_date_time, prompt: {day: 'Choose day', month: 'Choose month', year: 'Choose year'})
-
# select_datetime(my_date_time, prompt: {hour: true}) # generic prompt for hours
-
# select_datetime(my_date_time, prompt: true) # generic prompts for all
-
1
def select_datetime(datetime = Time.current, options = {}, html_options = {})
-
DateTimeSelector.new(datetime, options, html_options).select_datetime
-
end
-
-
# Returns a set of html select-tags (one for year, month, and day) pre-selected with the +date+.
-
# It's possible to explicitly set the order of the tags using the <tt>:order</tt> option with an array of
-
# symbols <tt>:year</tt>, <tt>:month</tt> and <tt>:day</tt> in the desired order.
-
# If the array passed to the <tt>:order</tt> option does not contain all the three symbols, all tags will be hidden.
-
#
-
# If anything is passed in the html_options hash it will be applied to every select tag in the set.
-
#
-
# my_date = Time.now + 6.days
-
#
-
# # Generates a date select that defaults to the date in my_date (six days after today).
-
# select_date(my_date)
-
#
-
# # Generates a date select that defaults to today (no specified date).
-
# select_date()
-
#
-
# # Generates a date select that defaults to the date in my_date (six days after today)
-
# # with the fields ordered year, month, day rather than month, day, year.
-
# select_date(my_date, order: [:year, :month, :day])
-
#
-
# # Generates a date select that discards the type of the field and defaults to the date in
-
# # my_date (six days after today).
-
# select_date(my_date, discard_type: true)
-
#
-
# # Generates a date select that defaults to the date in my_date,
-
# # which has fields separated by '/'.
-
# select_date(my_date, date_separator: '/')
-
#
-
# # Generates a date select that defaults to the datetime in my_date (six days after today)
-
# # prefixed with 'payday' rather than 'date'.
-
# select_date(my_date, prefix: 'payday')
-
#
-
# # Generates a date select with a custom prompt. Use <tt>prompt: true</tt> for generic prompts.
-
# select_date(my_date, prompt: {day: 'Choose day', month: 'Choose month', year: 'Choose year'})
-
# select_date(my_date, prompt: {hour: true}) # generic prompt for hours
-
# select_date(my_date, prompt: true) # generic prompts for all
-
1
def select_date(date = Date.current, options = {}, html_options = {})
-
DateTimeSelector.new(date, options, html_options).select_date
-
end
-
-
# Returns a set of html select-tags (one for hour and minute).
-
# You can set <tt>:time_separator</tt> key to format the output, and
-
# the <tt>:include_seconds</tt> option to include an input for seconds.
-
#
-
# If anything is passed in the html_options hash it will be applied to every select tag in the set.
-
#
-
# my_time = Time.now + 5.days + 7.hours + 3.minutes + 14.seconds
-
#
-
# # Generates a time select that defaults to the time in my_time.
-
# select_time(my_time)
-
#
-
# # Generates a time select that defaults to the current time (no specified time).
-
# select_time()
-
#
-
# # Generates a time select that defaults to the time in my_time,
-
# # which has fields separated by ':'.
-
# select_time(my_time, time_separator: ':')
-
#
-
# # Generates a time select that defaults to the time in my_time,
-
# # that also includes an input for seconds.
-
# select_time(my_time, include_seconds: true)
-
#
-
# # Generates a time select that defaults to the time in my_time, that has fields
-
# # separated by ':' and includes an input for seconds.
-
# select_time(my_time, time_separator: ':', include_seconds: true)
-
#
-
# # Generate a time select field with hours in the AM/PM format
-
# select_time(my_time, ampm: true)
-
#
-
# # Generates a time select field with hours that range from 2 to 14
-
# select_time(my_time, start_hour: 2, end_hour: 14)
-
#
-
# # Generates a time select with a custom prompt. Use <tt>:prompt</tt> to true for generic prompts.
-
# select_time(my_time, prompt: {day: 'Choose day', month: 'Choose month', year: 'Choose year'})
-
# select_time(my_time, prompt: {hour: true}) # generic prompt for hours
-
# select_time(my_time, prompt: true) # generic prompts for all
-
1
def select_time(datetime = Time.current, options = {}, html_options = {})
-
DateTimeSelector.new(datetime, options, html_options).select_time
-
end
-
-
# Returns a select tag with options for each of the seconds 0 through 59 with the current second selected.
-
# The <tt>datetime</tt> can be either a +Time+ or +DateTime+ object or an integer.
-
# Override the field name using the <tt>:field_name</tt> option, 'second' by default.
-
#
-
# my_time = Time.now + 16.minutes
-
#
-
# # Generates a select field for seconds that defaults to the seconds for the time in my_time.
-
# select_second(my_time)
-
#
-
# # Generates a select field for seconds that defaults to the number given.
-
# select_second(33)
-
#
-
# # Generates a select field for seconds that defaults to the seconds for the time in my_time
-
# # that is named 'interval' rather than 'second'.
-
# select_second(my_time, field_name: 'interval')
-
#
-
# # Generates a select field for seconds with a custom prompt. Use <tt>prompt: true</tt> for a
-
# # generic prompt.
-
# select_second(14, prompt: 'Choose seconds')
-
1
def select_second(datetime, options = {}, html_options = {})
-
DateTimeSelector.new(datetime, options, html_options).select_second
-
end
-
-
# Returns a select tag with options for each of the minutes 0 through 59 with the current minute selected.
-
# Also can return a select tag with options by <tt>minute_step</tt> from 0 through 59 with the 00 minute
-
# selected. The <tt>datetime</tt> can be either a +Time+ or +DateTime+ object or an integer.
-
# Override the field name using the <tt>:field_name</tt> option, 'minute' by default.
-
#
-
# my_time = Time.now + 6.hours
-
#
-
# # Generates a select field for minutes that defaults to the minutes for the time in my_time.
-
# select_minute(my_time)
-
#
-
# # Generates a select field for minutes that defaults to the number given.
-
# select_minute(14)
-
#
-
# # Generates a select field for minutes that defaults to the minutes for the time in my_time
-
# # that is named 'moment' rather than 'minute'.
-
# select_minute(my_time, field_name: 'moment')
-
#
-
# # Generates a select field for minutes with a custom prompt. Use <tt>prompt: true</tt> for a
-
# # generic prompt.
-
# select_minute(14, prompt: 'Choose minutes')
-
1
def select_minute(datetime, options = {}, html_options = {})
-
DateTimeSelector.new(datetime, options, html_options).select_minute
-
end
-
-
# Returns a select tag with options for each of the hours 0 through 23 with the current hour selected.
-
# The <tt>datetime</tt> can be either a +Time+ or +DateTime+ object or an integer.
-
# Override the field name using the <tt>:field_name</tt> option, 'hour' by default.
-
#
-
# my_time = Time.now + 6.hours
-
#
-
# # Generates a select field for hours that defaults to the hour for the time in my_time.
-
# select_hour(my_time)
-
#
-
# # Generates a select field for hours that defaults to the number given.
-
# select_hour(13)
-
#
-
# # Generates a select field for hours that defaults to the hour for the time in my_time
-
# # that is named 'stride' rather than 'hour'.
-
# select_hour(my_time, field_name: 'stride')
-
#
-
# # Generates a select field for hours with a custom prompt. Use <tt>prompt: true</tt> for a
-
# # generic prompt.
-
# select_hour(13, prompt: 'Choose hour')
-
#
-
# # Generate a select field for hours in the AM/PM format
-
# select_hour(my_time, ampm: true)
-
#
-
# # Generates a select field that includes options for hours from 2 to 14.
-
# select_hour(my_time, start_hour: 2, end_hour: 14)
-
1
def select_hour(datetime, options = {}, html_options = {})
-
DateTimeSelector.new(datetime, options, html_options).select_hour
-
end
-
-
# Returns a select tag with options for each of the days 1 through 31 with the current day selected.
-
# The <tt>date</tt> can also be substituted for a day number.
-
# If you want to display days with a leading zero set the <tt>:use_two_digit_numbers</tt> key in +options+ to true.
-
# Override the field name using the <tt>:field_name</tt> option, 'day' by default.
-
#
-
# my_date = Time.now + 2.days
-
#
-
# # Generates a select field for days that defaults to the day for the date in my_date.
-
# select_day(my_time)
-
#
-
# # Generates a select field for days that defaults to the number given.
-
# select_day(5)
-
#
-
# # Generates a select field for days that defaults to the number given, but displays it with two digits.
-
# select_day(5, use_two_digit_numbers: true)
-
#
-
# # Generates a select field for days that defaults to the day for the date in my_date
-
# # that is named 'due' rather than 'day'.
-
# select_day(my_time, field_name: 'due')
-
#
-
# # Generates a select field for days with a custom prompt. Use <tt>prompt: true</tt> for a
-
# # generic prompt.
-
# select_day(5, prompt: 'Choose day')
-
1
def select_day(date, options = {}, html_options = {})
-
DateTimeSelector.new(date, options, html_options).select_day
-
end
-
-
# Returns a select tag with options for each of the months January through December with the current month
-
# selected. The month names are presented as keys (what's shown to the user) and the month numbers (1-12) are
-
# used as values (what's submitted to the server). It's also possible to use month numbers for the presentation
-
# instead of names -- set the <tt>:use_month_numbers</tt> key in +options+ to true for this to happen. If you
-
# want both numbers and names, set the <tt>:add_month_numbers</tt> key in +options+ to true. If you would prefer
-
# to show month names as abbreviations, set the <tt>:use_short_month</tt> key in +options+ to true. If you want
-
# to use your own month names, set the <tt>:use_month_names</tt> key in +options+ to an array of 12 month names.
-
# If you want to display months with a leading zero set the <tt>:use_two_digit_numbers</tt> key in +options+ to true.
-
# Override the field name using the <tt>:field_name</tt> option, 'month' by default.
-
#
-
# # Generates a select field for months that defaults to the current month that
-
# # will use keys like "January", "March".
-
# select_month(Date.today)
-
#
-
# # Generates a select field for months that defaults to the current month that
-
# # is named "start" rather than "month".
-
# select_month(Date.today, field_name: 'start')
-
#
-
# # Generates a select field for months that defaults to the current month that
-
# # will use keys like "1", "3".
-
# select_month(Date.today, use_month_numbers: true)
-
#
-
# # Generates a select field for months that defaults to the current month that
-
# # will use keys like "1 - January", "3 - March".
-
# select_month(Date.today, add_month_numbers: true)
-
#
-
# # Generates a select field for months that defaults to the current month that
-
# # will use keys like "Jan", "Mar".
-
# select_month(Date.today, use_short_month: true)
-
#
-
# # Generates a select field for months that defaults to the current month that
-
# # will use keys like "Januar", "Marts."
-
# select_month(Date.today, use_month_names: %w(Januar Februar Marts ...))
-
#
-
# # Generates a select field for months that defaults to the current month that
-
# # will use keys with two digit numbers like "01", "03".
-
# select_month(Date.today, use_two_digit_numbers: true)
-
#
-
# # Generates a select field for months with a custom prompt. Use <tt>prompt: true</tt> for a
-
# # generic prompt.
-
# select_month(14, prompt: 'Choose month')
-
1
def select_month(date, options = {}, html_options = {})
-
DateTimeSelector.new(date, options, html_options).select_month
-
end
-
-
# Returns a select tag with options for each of the five years on each side of the current, which is selected.
-
# The five year radius can be changed using the <tt>:start_year</tt> and <tt>:end_year</tt> keys in the
-
# +options+. Both ascending and descending year lists are supported by making <tt>:start_year</tt> less than or
-
# greater than <tt>:end_year</tt>. The <tt>date</tt> can also be substituted for a year given as a number.
-
# Override the field name using the <tt>:field_name</tt> option, 'year' by default.
-
#
-
# # Generates a select field for years that defaults to the current year that
-
# # has ascending year values.
-
# select_year(Date.today, start_year: 1992, end_year: 2007)
-
#
-
# # Generates a select field for years that defaults to the current year that
-
# # is named 'birth' rather than 'year'.
-
# select_year(Date.today, field_name: 'birth')
-
#
-
# # Generates a select field for years that defaults to the current year that
-
# # has descending year values.
-
# select_year(Date.today, start_year: 2005, end_year: 1900)
-
#
-
# # Generates a select field for years that defaults to the year 2006 that
-
# # has ascending year values.
-
# select_year(2006, start_year: 2000, end_year: 2010)
-
#
-
# # Generates a select field for years with a custom prompt. Use <tt>prompt: true</tt> for a
-
# # generic prompt.
-
# select_year(14, prompt: 'Choose year')
-
1
def select_year(date, options = {}, html_options = {})
-
DateTimeSelector.new(date, options, html_options).select_year
-
end
-
-
# Returns an html time tag for the given date or time.
-
#
-
# time_tag Date.today # =>
-
# <time datetime="2010-11-04">November 04, 2010</time>
-
# time_tag Time.now # =>
-
# <time datetime="2010-11-04T17:55:45+01:00">November 04, 2010 17:55</time>
-
# time_tag Date.yesterday, 'Yesterday' # =>
-
# <time datetime="2010-11-03">Yesterday</time>
-
# time_tag Date.today, pubdate: true # =>
-
# <time datetime="2010-11-04" pubdate="pubdate">November 04, 2010</time>
-
#
-
# <%= time_tag Time.now do %>
-
# <span>Right now</span>
-
# <% end %>
-
# # => <time datetime="2010-11-04T17:55:45+01:00"><span>Right now</span></time>
-
1
def time_tag(date_or_time, *args, &block)
-
options = args.extract_options!
-
format = options.delete(:format) || :long
-
content = args.first || I18n.l(date_or_time, :format => format)
-
datetime = date_or_time.acts_like?(:time) ? date_or_time.xmlschema : date_or_time.rfc3339
-
-
content_tag(:time, content, options.reverse_merge(:datetime => datetime), &block)
-
end
-
end
-
-
1
class DateTimeSelector #:nodoc:
-
1
include ActionView::Helpers::TagHelper
-
-
1
DEFAULT_PREFIX = 'date'.freeze
-
1
POSITION = {
-
:year => 1, :month => 2, :day => 3, :hour => 4, :minute => 5, :second => 6
-
}.freeze
-
-
1
AMPM_TRANSLATION = Hash[
-
[[0, "12 AM"], [1, "01 AM"], [2, "02 AM"], [3, "03 AM"],
-
[4, "04 AM"], [5, "05 AM"], [6, "06 AM"], [7, "07 AM"],
-
[8, "08 AM"], [9, "09 AM"], [10, "10 AM"], [11, "11 AM"],
-
[12, "12 PM"], [13, "01 PM"], [14, "02 PM"], [15, "03 PM"],
-
[16, "04 PM"], [17, "05 PM"], [18, "06 PM"], [19, "07 PM"],
-
[20, "08 PM"], [21, "09 PM"], [22, "10 PM"], [23, "11 PM"]]
-
].freeze
-
-
1
def initialize(datetime, options = {}, html_options = {})
-
@options = options.dup
-
@html_options = html_options.dup
-
@datetime = datetime
-
@options[:datetime_separator] ||= ' — '
-
@options[:time_separator] ||= ' : '
-
end
-
-
1
def select_datetime
-
order = date_order.dup
-
order -= [:hour, :minute, :second]
-
@options[:discard_year] ||= true unless order.include?(:year)
-
@options[:discard_month] ||= true unless order.include?(:month)
-
@options[:discard_day] ||= true if @options[:discard_month] || !order.include?(:day)
-
@options[:discard_minute] ||= true if @options[:discard_hour]
-
@options[:discard_second] ||= true unless @options[:include_seconds] && !@options[:discard_minute]
-
-
set_day_if_discarded
-
-
if @options[:tag] && @options[:ignore_date]
-
select_time
-
else
-
[:day, :month, :year].each { |o| order.unshift(o) unless order.include?(o) }
-
order += [:hour, :minute, :second] unless @options[:discard_hour]
-
-
build_selects_from_types(order)
-
end
-
end
-
-
1
def select_date
-
order = date_order.dup
-
-
@options[:discard_hour] = true
-
@options[:discard_minute] = true
-
@options[:discard_second] = true
-
-
@options[:discard_year] ||= true unless order.include?(:year)
-
@options[:discard_month] ||= true unless order.include?(:month)
-
@options[:discard_day] ||= true if @options[:discard_month] || !order.include?(:day)
-
-
set_day_if_discarded
-
-
[:day, :month, :year].each { |o| order.unshift(o) unless order.include?(o) }
-
-
build_selects_from_types(order)
-
end
-
-
1
def select_time
-
order = []
-
-
@options[:discard_month] = true
-
@options[:discard_year] = true
-
@options[:discard_day] = true
-
@options[:discard_second] ||= true unless @options[:include_seconds]
-
-
order += [:year, :month, :day] unless @options[:ignore_date]
-
-
order += [:hour, :minute]
-
order << :second if @options[:include_seconds]
-
-
build_selects_from_types(order)
-
end
-
-
1
def select_second
-
if @options[:use_hidden] || @options[:discard_second]
-
build_hidden(:second, sec) if @options[:include_seconds]
-
else
-
build_options_and_select(:second, sec)
-
end
-
end
-
-
1
def select_minute
-
if @options[:use_hidden] || @options[:discard_minute]
-
build_hidden(:minute, min)
-
else
-
build_options_and_select(:minute, min, :step => @options[:minute_step])
-
end
-
end
-
-
1
def select_hour
-
if @options[:use_hidden] || @options[:discard_hour]
-
build_hidden(:hour, hour)
-
else
-
options = {}
-
options[:ampm] = @options[:ampm] || false
-
options[:start] = @options[:start_hour] || 0
-
options[:end] = @options[:end_hour] || 23
-
build_options_and_select(:hour, hour, options)
-
end
-
end
-
-
1
def select_day
-
if @options[:use_hidden] || @options[:discard_day]
-
build_hidden(:day, day || 1)
-
else
-
build_options_and_select(:day, day, :start => 1, :end => 31, :leading_zeros => false, :use_two_digit_numbers => @options[:use_two_digit_numbers])
-
end
-
end
-
-
1
def select_month
-
if @options[:use_hidden] || @options[:discard_month]
-
build_hidden(:month, month || 1)
-
else
-
month_options = []
-
1.upto(12) do |month_number|
-
options = { :value => month_number }
-
options[:selected] = "selected" if month == month_number
-
month_options << content_tag(:option, month_name(month_number), options) + "\n"
-
end
-
build_select(:month, month_options.join)
-
end
-
end
-
-
1
def select_year
-
if !@datetime || @datetime == 0
-
val = '1'
-
middle_year = Date.today.year
-
else
-
val = middle_year = year
-
end
-
-
if @options[:use_hidden] || @options[:discard_year]
-
build_hidden(:year, val)
-
else
-
options = {}
-
options[:start] = @options[:start_year] || middle_year - 5
-
options[:end] = @options[:end_year] || middle_year + 5
-
options[:step] = options[:start] < options[:end] ? 1 : -1
-
options[:leading_zeros] = false
-
options[:max_years_allowed] = @options[:max_years_allowed] || 1000
-
-
if (options[:end] - options[:start]).abs > options[:max_years_allowed]
-
raise ArgumentError, "There're too many years options to be built. Are you sure you haven't mistyped something? You can provide the :max_years_allowed parameter"
-
end
-
-
build_options_and_select(:year, val, options)
-
end
-
end
-
-
1
private
-
1
%w( sec min hour day month year ).each do |method|
-
6
define_method(method) do
-
@datetime.kind_of?(Numeric) ? @datetime : @datetime.send(method) if @datetime
-
end
-
end
-
-
# If the day is hidden, the day should be set to the 1st so all month and year choices are
-
# valid. Otherwise, February 31st or February 29th, 2011 can be selected, which are invalid.
-
1
def set_day_if_discarded
-
if @datetime && @options[:discard_day]
-
@datetime = @datetime.change(:day => 1)
-
end
-
end
-
-
# Returns translated month names, but also ensures that a custom month
-
# name array has a leading nil element.
-
1
def month_names
-
@month_names ||= begin
-
month_names = @options[:use_month_names] || translated_month_names
-
month_names.unshift(nil) if month_names.size < 13
-
month_names
-
end
-
end
-
-
# Returns translated month names.
-
# => [nil, "January", "February", "March",
-
# "April", "May", "June", "July",
-
# "August", "September", "October",
-
# "November", "December"]
-
#
-
# If <tt>:use_short_month</tt> option is set
-
# => [nil, "Jan", "Feb", "Mar", "Apr", "May", "Jun",
-
# "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
-
1
def translated_month_names
-
key = @options[:use_short_month] ? :'date.abbr_month_names' : :'date.month_names'
-
I18n.translate(key, :locale => @options[:locale])
-
end
-
-
# Lookup month name for number.
-
# month_name(1) => "January"
-
#
-
# If <tt>:use_month_numbers</tt> option is passed
-
# month_name(1) => 1
-
#
-
# If <tt>:use_two_month_numbers</tt> option is passed
-
# month_name(1) => '01'
-
#
-
# If <tt>:add_month_numbers</tt> option is passed
-
# month_name(1) => "1 - January"
-
1
def month_name(number)
-
if @options[:use_month_numbers]
-
number
-
elsif @options[:use_two_digit_numbers]
-
sprintf "%02d", number
-
elsif @options[:add_month_numbers]
-
"#{number} - #{month_names[number]}"
-
else
-
month_names[number]
-
end
-
end
-
-
1
def date_order
-
@date_order ||= @options[:order] || translated_date_order
-
end
-
-
1
def translated_date_order
-
date_order = I18n.translate(:'date.order', :locale => @options[:locale], :default => [])
-
-
forbidden_elements = date_order - [:year, :month, :day]
-
if forbidden_elements.any?
-
raise StandardError,
-
"#{@options[:locale]}.date.order only accepts :year, :month and :day"
-
end
-
-
date_order
-
end
-
-
# Build full select tag from date type and options.
-
1
def build_options_and_select(type, selected, options = {})
-
build_select(type, build_options(selected, options))
-
end
-
-
# Build select option html from date value and options.
-
# build_options(15, start: 1, end: 31)
-
# => "<option value="1">1</option>
-
# <option value="2">2</option>
-
# <option value="3">3</option>..."
-
#
-
# If <tt>use_two_digit_numbers: true</tt> option is passed
-
# build_options(15, start: 1, end: 31, use_two_digit_numbers: true)
-
# => "<option value="1">01</option>
-
# <option value="2">02</option>
-
# <option value="3">03</option>..."
-
#
-
# If <tt>:step</tt> options is passed
-
# build_options(15, start: 1, end: 31, step: 2)
-
# => "<option value="1">1</option>
-
# <option value="3">3</option>
-
# <option value="5">5</option>..."
-
1
def build_options(selected, options = {})
-
options = {
-
leading_zeros: true, ampm: false, use_two_digit_numbers: false
-
}.merge!(options)
-
-
start = options.delete(:start) || 0
-
stop = options.delete(:end) || 59
-
step = options.delete(:step) || 1
-
leading_zeros = options.delete(:leading_zeros)
-
-
select_options = []
-
start.step(stop, step) do |i|
-
value = leading_zeros ? sprintf("%02d", i) : i
-
tag_options = { :value => value }
-
tag_options[:selected] = "selected" if selected == i
-
text = options[:use_two_digit_numbers] ? sprintf("%02d", i) : value
-
text = options[:ampm] ? AMPM_TRANSLATION[i] : text
-
select_options << content_tag(:option, text, tag_options)
-
end
-
-
(select_options.join("\n") + "\n").html_safe
-
end
-
-
# Builds select tag from date type and html select options.
-
# build_select(:month, "<option value="1">January</option>...")
-
# => "<select id="post_written_on_2i" name="post[written_on(2i)]">
-
# <option value="1">January</option>...
-
# </select>"
-
1
def build_select(type, select_options_as_html)
-
select_options = {
-
:id => input_id_from_type(type),
-
:name => input_name_from_type(type)
-
}.merge!(@html_options)
-
select_options[:disabled] = 'disabled' if @options[:disabled]
-
select_options[:class] = type if @options[:with_css_classes]
-
-
select_html = "\n"
-
select_html << content_tag(:option, '', :value => '') + "\n" if @options[:include_blank]
-
select_html << prompt_option_tag(type, @options[:prompt]) + "\n" if @options[:prompt]
-
select_html << select_options_as_html
-
-
(content_tag(:select, select_html.html_safe, select_options) + "\n").html_safe
-
end
-
-
# Builds a prompt option tag with supplied options or from default options.
-
# prompt_option_tag(:month, prompt: 'Select month')
-
# => "<option value="">Select month</option>"
-
1
def prompt_option_tag(type, options)
-
prompt = case options
-
when Hash
-
default_options = {:year => false, :month => false, :day => false, :hour => false, :minute => false, :second => false}
-
default_options.merge!(options)[type.to_sym]
-
when String
-
options
-
else
-
I18n.translate(:"datetime.prompts.#{type}", :locale => @options[:locale])
-
end
-
-
prompt ? content_tag(:option, prompt, :value => '') : ''
-
end
-
-
# Builds hidden input tag for date part and value.
-
# build_hidden(:year, 2008)
-
# => "<input id="post_written_on_1i" name="post[written_on(1i)]" type="hidden" value="2008" />"
-
1
def build_hidden(type, value)
-
select_options = {
-
:type => "hidden",
-
:id => input_id_from_type(type),
-
:name => input_name_from_type(type),
-
:value => value
-
}.merge!(@html_options.slice(:disabled))
-
select_options[:disabled] = 'disabled' if @options[:disabled]
-
-
tag(:input, select_options) + "\n".html_safe
-
end
-
-
# Returns the name attribute for the input tag.
-
# => post[written_on(1i)]
-
1
def input_name_from_type(type)
-
prefix = @options[:prefix] || ActionView::Helpers::DateTimeSelector::DEFAULT_PREFIX
-
prefix += "[#{@options[:index]}]" if @options.has_key?(:index)
-
-
field_name = @options[:field_name] || type
-
if @options[:include_position]
-
field_name += "(#{ActionView::Helpers::DateTimeSelector::POSITION[type]}i)"
-
end
-
-
@options[:discard_type] ? prefix : "#{prefix}[#{field_name}]"
-
end
-
-
# Returns the id attribute for the input tag.
-
# => "post_written_on_1i"
-
1
def input_id_from_type(type)
-
id = input_name_from_type(type).gsub(/([\[\(])|(\]\[)/, '_').gsub(/[\]\)]/, '')
-
id = @options[:namespace] + '_' + id if @options[:namespace]
-
-
id
-
end
-
-
# Given an ordering of datetime components, create the selection HTML
-
# and join them with their appropriate separators.
-
1
def build_selects_from_types(order)
-
select = ''
-
first_visible = order.find { |type| !@options[:"discard_#{type}"] }
-
order.reverse.each do |type|
-
separator = separator(type) unless type == first_visible # don't add before first visible field
-
select.insert(0, separator.to_s + send("select_#{type}").to_s)
-
end
-
select.html_safe
-
end
-
-
# Returns the separator for a given datetime component.
-
1
def separator(type)
-
return "" if @options[:use_hidden]
-
-
case type
-
when :year, :month, :day
-
@options[:"discard_#{type}"] ? "" : @options[:date_separator]
-
when :hour
-
(@options[:discard_year] && @options[:discard_day]) ? "" : @options[:datetime_separator]
-
when :minute, :second
-
@options[:"discard_#{type}"] ? "" : @options[:time_separator]
-
end
-
end
-
end
-
-
1
class FormBuilder
-
1
def date_select(method, options = {}, html_options = {})
-
@template.date_select(@object_name, method, objectify_options(options), html_options)
-
end
-
-
1
def time_select(method, options = {}, html_options = {})
-
@template.time_select(@object_name, method, objectify_options(options), html_options)
-
end
-
-
1
def datetime_select(method, options = {}, html_options = {})
-
@template.datetime_select(@object_name, method, objectify_options(options), html_options)
-
end
-
end
-
end
-
end
-
1
module ActionView
-
# = Action View Debug Helper
-
#
-
# Provides a set of methods for making it easier to debug Rails objects.
-
1
module Helpers
-
1
module DebugHelper
-
-
1
include TagHelper
-
-
# Returns a YAML representation of +object+ wrapped with <pre> and </pre>.
-
# If the object cannot be converted to YAML using +to_yaml+, +inspect+ will be called instead.
-
# Useful for inspecting an object at the time of rendering.
-
#
-
# @user = User.new({ username: 'testing', password: 'xyz', age: 42}) %>
-
# debug(@user)
-
# # =>
-
# <pre class='debug_dump'>--- !ruby/object:User
-
# attributes:
-
# updated_at:
-
# username: testing
-
#
-
# age: 42
-
# password: xyz
-
# created_at:
-
# attributes_cache: {}
-
#
-
# new_record: true
-
# </pre>
-
1
def debug(object)
-
begin
-
Marshal::dump(object)
-
object = ERB::Util.html_escape(object.to_yaml).gsub(" ", " ").html_safe
-
content_tag(:pre, object, :class => "debug_dump")
-
rescue Exception # errors from Marshal or YAML
-
# Object couldn't be dumped, perhaps because of singleton methods -- this is the fallback
-
content_tag(:code, object.to_yaml, :class => "debug_dump")
-
end
-
end
-
end
-
end
-
end
-
1
require 'cgi'
-
1
require 'action_view/helpers/date_helper'
-
1
require 'action_view/helpers/tag_helper'
-
1
require 'action_view/helpers/form_tag_helper'
-
1
require 'action_view/helpers/active_model_helper'
-
1
require 'action_view/helpers/tags'
-
1
require 'action_view/model_naming'
-
1
require 'active_support/core_ext/class/attribute_accessors'
-
1
require 'active_support/core_ext/hash/slice'
-
1
require 'active_support/core_ext/string/output_safety'
-
1
require 'active_support/core_ext/array/extract_options'
-
1
require 'active_support/core_ext/string/inflections'
-
-
1
module ActionView
-
# = Action View Form Helpers
-
1
module Helpers
-
# Form helpers are designed to make working with resources much easier
-
# compared to using vanilla HTML.
-
#
-
# Typically, a form designed to create or update a resource reflects the
-
# identity of the resource in several ways: (i) the url that the form is
-
# sent to (the form element's +action+ attribute) should result in a request
-
# being routed to the appropriate controller action (with the appropriate <tt>:id</tt>
-
# parameter in the case of an existing resource), (ii) input fields should
-
# be named in such a way that in the controller their values appear in the
-
# appropriate places within the +params+ hash, and (iii) for an existing record,
-
# when the form is initially displayed, input fields corresponding to attributes
-
# of the resource should show the current values of those attributes.
-
#
-
# In Rails, this is usually achieved by creating the form using +form_for+ and
-
# a number of related helper methods. +form_for+ generates an appropriate <tt>form</tt>
-
# tag and yields a form builder object that knows the model the form is about.
-
# Input fields are created by calling methods defined on the form builder, which
-
# means they are able to generate the appropriate names and default values
-
# corresponding to the model attributes, as well as convenient IDs, etc.
-
# Conventions in the generated field names allow controllers to receive form data
-
# nicely structured in +params+ with no effort on your side.
-
#
-
# For example, to create a new person you typically set up a new instance of
-
# +Person+ in the <tt>PeopleController#new</tt> action, <tt>@person</tt>, and
-
# in the view template pass that object to +form_for+:
-
#
-
# <%= form_for @person do |f| %>
-
# <%= f.label :first_name %>:
-
# <%= f.text_field :first_name %><br />
-
#
-
# <%= f.label :last_name %>:
-
# <%= f.text_field :last_name %><br />
-
#
-
# <%= f.submit %>
-
# <% end %>
-
#
-
# The HTML generated for this would be (modulus formatting):
-
#
-
# <form action="/people" class="new_person" id="new_person" method="post">
-
# <div style="margin:0;padding:0;display:inline">
-
# <input name="authenticity_token" type="hidden" value="NrOp5bsjoLRuK8IW5+dQEYjKGUJDe7TQoZVvq95Wteg=" />
-
# </div>
-
# <label for="person_first_name">First name</label>:
-
# <input id="person_first_name" name="person[first_name]" type="text" /><br />
-
#
-
# <label for="person_last_name">Last name</label>:
-
# <input id="person_last_name" name="person[last_name]" type="text" /><br />
-
#
-
# <input name="commit" type="submit" value="Create Person" />
-
# </form>
-
#
-
# As you see, the HTML reflects knowledge about the resource in several spots,
-
# like the path the form should be submitted to, or the names of the input fields.
-
#
-
# In particular, thanks to the conventions followed in the generated field names, the
-
# controller gets a nested hash <tt>params[:person]</tt> with the person attributes
-
# set in the form. That hash is ready to be passed to <tt>Person.create</tt>:
-
#
-
# if @person = Person.create(params[:person])
-
# # success
-
# else
-
# # error handling
-
# end
-
#
-
# Interestingly, the exact same view code in the previous example can be used to edit
-
# a person. If <tt>@person</tt> is an existing record with name "John Smith" and ID 256,
-
# the code above as is would yield instead:
-
#
-
# <form action="/people/256" class="edit_person" id="edit_person_256" method="post">
-
# <div style="margin:0;padding:0;display:inline">
-
# <input name="_method" type="hidden" value="put" />
-
# <input name="authenticity_token" type="hidden" value="NrOp5bsjoLRuK8IW5+dQEYjKGUJDe7TQoZVvq95Wteg=" />
-
# </div>
-
# <label for="person_first_name">First name</label>:
-
# <input id="person_first_name" name="person[first_name]" type="text" value="John" /><br />
-
#
-
# <label for="person_last_name">Last name</label>:
-
# <input id="person_last_name" name="person[last_name]" type="text" value="Smith" /><br />
-
#
-
# <input name="commit" type="submit" value="Update Person" />
-
# </form>
-
#
-
# Note that the endpoint, default values, and submit button label are tailored for <tt>@person</tt>.
-
# That works that way because the involved helpers know whether the resource is a new record or not,
-
# and generate HTML accordingly.
-
#
-
# The controller would receive the form data again in <tt>params[:person]</tt>, ready to be
-
# passed to <tt>Person#update_attributes</tt>:
-
#
-
# if @person.update_attributes(params[:person])
-
# # success
-
# else
-
# # error handling
-
# end
-
#
-
# That's how you typically work with resources.
-
1
module FormHelper
-
1
extend ActiveSupport::Concern
-
-
1
include FormTagHelper
-
1
include UrlHelper
-
1
include ModelNaming
-
-
# Creates a form that allows the user to create or update the attributes
-
# of a specific model object.
-
#
-
# The method can be used in several slightly different ways, depending on
-
# how much you wish to rely on Rails to infer automatically from the model
-
# how the form should be constructed. For a generic model object, a form
-
# can be created by passing +form_for+ a string or symbol representing
-
# the object we are concerned with:
-
#
-
# <%= form_for :person do |f| %>
-
# First name: <%= f.text_field :first_name %><br />
-
# Last name : <%= f.text_field :last_name %><br />
-
# Biography : <%= f.text_area :biography %><br />
-
# Admin? : <%= f.check_box :admin %><br />
-
# <%= f.submit %>
-
# <% end %>
-
#
-
# The variable +f+ yielded to the block is a FormBuilder object that
-
# incorporates the knowledge about the model object represented by
-
# <tt>:person</tt> passed to +form_for+. Methods defined on the FormBuilder
-
# are used to generate fields bound to this model. Thus, for example,
-
#
-
# <%= f.text_field :first_name %>
-
#
-
# will get expanded to
-
#
-
# <%= text_field :person, :first_name %>
-
# which results in an html <tt><input></tt> tag whose +name+ attribute is
-
# <tt>person[first_name]</tt>. This means that when the form is submitted,
-
# the value entered by the user will be available in the controller as
-
# <tt>params[:person][:first_name]</tt>.
-
#
-
# For fields generated in this way using the FormBuilder,
-
# if <tt>:person</tt> also happens to be the name of an instance variable
-
# <tt>@person</tt>, the default value of the field shown when the form is
-
# initially displayed (e.g. in the situation where you are editing an
-
# existing record) will be the value of the corresponding attribute of
-
# <tt>@person</tt>.
-
#
-
# The rightmost argument to +form_for+ is an
-
# optional hash of options -
-
#
-
# * <tt>:url</tt> - The URL the form is to be submitted to. This may be
-
# represented in the same way as values passed to +url_for+ or +link_to+.
-
# So for example you may use a named route directly. When the model is
-
# represented by a string or symbol, as in the example above, if the
-
# <tt>:url</tt> option is not specified, by default the form will be
-
# sent back to the current url (We will describe below an alternative
-
# resource-oriented usage of +form_for+ in which the URL does not need
-
# to be specified explicitly).
-
# * <tt>:namespace</tt> - A namespace for your form to ensure uniqueness of
-
# id attributes on form elements. The namespace attribute will be prefixed
-
# with underscore on the generated HTML id.
-
# * <tt>:html</tt> - Optional HTML attributes for the form tag.
-
#
-
# Also note that +form_for+ doesn't create an exclusive scope. It's still
-
# possible to use both the stand-alone FormHelper methods and methods
-
# from FormTagHelper. For example:
-
#
-
# <%= form_for :person do |f| %>
-
# First name: <%= f.text_field :first_name %>
-
# Last name : <%= f.text_field :last_name %>
-
# Biography : <%= text_area :person, :biography %>
-
# Admin? : <%= check_box_tag "person[admin]", "1", @person.company.admin? %>
-
# <%= f.submit %>
-
# <% end %>
-
#
-
# This also works for the methods in FormOptionHelper and DateHelper that
-
# are designed to work with an object as base, like
-
# FormOptionHelper#collection_select and DateHelper#datetime_select.
-
#
-
# === #form_for with a model object
-
#
-
# In the examples above, the object to be created or edited was
-
# represented by a symbol passed to +form_for+, and we noted that
-
# a string can also be used equivalently. It is also possible, however,
-
# to pass a model object itself to +form_for+. For example, if <tt>@post</tt>
-
# is an existing record you wish to edit, you can create the form using
-
#
-
# <%= form_for @post do |f| %>
-
# ...
-
# <% end %>
-
#
-
# This behaves in almost the same way as outlined previously, with a
-
# couple of small exceptions. First, the prefix used to name the input
-
# elements within the form (hence the key that denotes them in the +params+
-
# hash) is actually derived from the object's _class_, e.g. <tt>params[:post]</tt>
-
# if the object's class is +Post+. However, this can be overwritten using
-
# the <tt>:as</tt> option, e.g. -
-
#
-
# <%= form_for(@person, as: :client) do |f| %>
-
# ...
-
# <% end %>
-
#
-
# would result in <tt>params[:client]</tt>.
-
#
-
# Secondly, the field values shown when the form is initially displayed
-
# are taken from the attributes of the object passed to +form_for+,
-
# regardless of whether the object is an instance
-
# variable. So, for example, if we had a _local_ variable +post+
-
# representing an existing record,
-
#
-
# <%= form_for post do |f| %>
-
# ...
-
# <% end %>
-
#
-
# would produce a form with fields whose initial state reflect the current
-
# values of the attributes of +post+.
-
#
-
# === Resource-oriented style
-
#
-
# In the examples just shown, although not indicated explicitly, we still
-
# need to use the <tt>:url</tt> option in order to specify where the
-
# form is going to be sent. However, further simplification is possible
-
# if the record passed to +form_for+ is a _resource_, i.e. it corresponds
-
# to a set of RESTful routes, e.g. defined using the +resources+ method
-
# in <tt>config/routes.rb</tt>. In this case Rails will simply infer the
-
# appropriate URL from the record itself. For example,
-
#
-
# <%= form_for @post do |f| %>
-
# ...
-
# <% end %>
-
#
-
# is then equivalent to something like:
-
#
-
# <%= form_for @post, as: :post, url: post_path(@post), method: :put, html: { class: "edit_post", id: "edit_post_45" } do |f| %>
-
# ...
-
# <% end %>
-
#
-
# And for a new record
-
#
-
# <%= form_for(Post.new) do |f| %>
-
# ...
-
# <% end %>
-
#
-
# is equivalent to something like:
-
#
-
# <%= form_for @post, as: :post, url: posts_path, html: { class: "new_post", id: "new_post" } do |f| %>
-
# ...
-
# <% end %>
-
#
-
# However you can still overwrite individual conventions, such as:
-
#
-
# <%= form_for(@post, url: super_posts_path) do |f| %>
-
# ...
-
# <% end %>
-
#
-
# You can also set the answer format, like this:
-
#
-
# <%= form_for(@post, format: :json) do |f| %>
-
# ...
-
# <% end %>
-
#
-
# For namespaced routes, like +admin_post_url+:
-
#
-
# <%= form_for([:admin, @post]) do |f| %>
-
# ...
-
# <% end %>
-
#
-
# If your resource has associations defined, for example, you want to add comments
-
# to the document given that the routes are set correctly:
-
#
-
# <%= form_for([@document, @comment]) do |f| %>
-
# ...
-
# <% end %>
-
#
-
# Where <tt>@document = Document.find(params[:id])</tt> and
-
# <tt>@comment = Comment.new</tt>.
-
#
-
# === Setting the method
-
#
-
# You can force the form to use the full array of HTTP verbs by setting
-
#
-
# method: (:get|:post|:patch|:put|:delete)
-
#
-
# in the options hash. If the verb is not GET or POST, which are natively
-
# supported by HTML forms, the form will be set to POST and a hidden input
-
# called _method will carry the intended verb for the server to interpret.
-
#
-
# === Unobtrusive JavaScript
-
#
-
# Specifying:
-
#
-
# remote: true
-
#
-
# in the options hash creates a form that will allow the unobtrusive JavaScript drivers to modify its
-
# behavior. The expected default behavior is an XMLHttpRequest in the background instead of the regular
-
# POST arrangement, but ultimately the behavior is the choice of the JavaScript driver implementor.
-
# Even though it's using JavaScript to serialize the form elements, the form submission will work just like
-
# a regular submission as viewed by the receiving side (all elements available in <tt>params</tt>).
-
#
-
# Example:
-
#
-
# <%= form_for(@post, remote: true) do |f| %>
-
# ...
-
# <% end %>
-
#
-
# The HTML generated for this would be:
-
#
-
# <form action='http://www.example.com' method='post' data-remote='true'>
-
# <div style='margin:0;padding:0;display:inline'>
-
# <input name='_method' type='hidden' value='put' />
-
# </div>
-
# ...
-
# </form>
-
#
-
# === Setting HTML options
-
#
-
# You can set data attributes directly by passing in a data hash, but all other HTML options must be wrapped in
-
# the HTML key. Example:
-
#
-
# <%= form_for(@post, data: { behavior: "autosave" }, html: { name: "go" }) do |f| %>
-
# ...
-
# <% end %>
-
#
-
# The HTML generated for this would be:
-
#
-
# <form action='http://www.example.com' method='post' data-behavior='autosave' name='go'>
-
# <div style='margin:0;padding:0;display:inline'>
-
# <input name='_method' type='hidden' value='put' />
-
# </div>
-
# ...
-
# </form>
-
#
-
# === Removing hidden model id's
-
#
-
# The form_for method automatically includes the model id as a hidden field in the form.
-
# This is used to maintain the correlation between the form data and its associated model.
-
# Some ORM systems do not use IDs on nested models so in this case you want to be able
-
# to disable the hidden id.
-
#
-
# In the following example the Post model has many Comments stored within it in a NoSQL database,
-
# thus there is no primary key for comments.
-
#
-
# Example:
-
#
-
# <%= form_for(@post) do |f| %>
-
# <%= f.fields_for(:comments, include_id: false) do |cf| %>
-
# ...
-
# <% end %>
-
# <% end %>
-
#
-
# === Customized form builders
-
#
-
# You can also build forms using a customized FormBuilder class. Subclass
-
# FormBuilder and override or define some more helpers, then use your
-
# custom builder. For example, let's say you made a helper to
-
# automatically add labels to form inputs.
-
#
-
# <%= form_for @person, url: { action: "create" }, builder: LabellingFormBuilder do |f| %>
-
# <%= f.text_field :first_name %>
-
# <%= f.text_field :last_name %>
-
# <%= f.text_area :biography %>
-
# <%= f.check_box :admin %>
-
# <%= f.submit %>
-
# <% end %>
-
#
-
# In this case, if you use this:
-
#
-
# <%= render f %>
-
#
-
# The rendered template is <tt>people/_labelling_form</tt> and the local
-
# variable referencing the form builder is called
-
# <tt>labelling_form</tt>.
-
#
-
# The custom FormBuilder class is automatically merged with the options
-
# of a nested fields_for call, unless it's explicitly set.
-
#
-
# In many cases you will want to wrap the above in another helper, so you
-
# could do something like the following:
-
#
-
# def labelled_form_for(record_or_name_or_array, *args, &proc)
-
# options = args.extract_options!
-
# form_for(record_or_name_or_array, *(args << options.merge(builder: LabellingFormBuilder)), &proc)
-
# end
-
#
-
# If you don't need to attach a form to a model instance, then check out
-
# FormTagHelper#form_tag.
-
#
-
# === Form to external resources
-
#
-
# When you build forms to external resources sometimes you need to set an authenticity token or just render a form
-
# without it, for example when you submit data to a payment gateway number and types of fields could be limited.
-
#
-
# To set an authenticity token you need to pass an <tt>:authenticity_token</tt> parameter
-
#
-
# <%= form_for @invoice, url: external_url, authenticity_token: 'external_token' do |f|
-
# ...
-
# <% end %>
-
#
-
# If you don't want to an authenticity token field be rendered at all just pass <tt>false</tt>:
-
#
-
# <%= form_for @invoice, url: external_url, authenticity_token: false do |f|
-
# ...
-
# <% end %>
-
1
def form_for(record, options = {}, &proc)
-
raise ArgumentError, "Missing block" unless block_given?
-
-
options[:html] ||= {}
-
-
case record
-
when String, Symbol
-
object_name = record
-
object = nil
-
else
-
object = record.is_a?(Array) ? record.last : record
-
raise ArgumentError, "First argument in form cannot contain nil or be empty" unless object
-
object_name = options[:as] || model_name_from_record_or_class(object).param_key
-
apply_form_for_options!(record, object, options)
-
end
-
-
options[:html][:data] = options.delete(:data) if options.has_key?(:data)
-
options[:html][:remote] = options.delete(:remote) if options.has_key?(:remote)
-
options[:html][:method] = options.delete(:method) if options.has_key?(:method)
-
options[:html][:authenticity_token] = options.delete(:authenticity_token)
-
-
builder = options[:parent_builder] = instantiate_builder(object_name, object, options)
-
fields_for = fields_for(object_name, object, options, &proc)
-
default_options = builder.multipart? ? { multipart: true } : {}
-
default_options.merge!(options.delete(:html))
-
-
form_tag(options.delete(:url) || {}, default_options) { fields_for }
-
end
-
-
1
def apply_form_for_options!(record, object, options) #:nodoc:
-
object = convert_to_model(object)
-
-
as = options[:as]
-
action, method = object.respond_to?(:persisted?) && object.persisted? ? [:edit, :patch] : [:new, :post]
-
options[:html].reverse_merge!(
-
class: as ? "#{action}_#{as}" : dom_class(object, action),
-
id: as ? "#{action}_#{as}" : [options[:namespace], dom_id(object, action)].compact.join("_").presence,
-
method: method
-
)
-
-
options[:url] ||= polymorphic_path(record, format: options.delete(:format))
-
end
-
1
private :apply_form_for_options!
-
-
# Creates a scope around a specific model object like form_for, but
-
# doesn't create the form tags themselves. This makes fields_for suitable
-
# for specifying additional model objects in the same form.
-
#
-
# === Generic Examples
-
#
-
# Although the usage and purpose of +field_for+ is similar to +form_for+'s,
-
# its method signature is slightly different. Like +form_for+, it yields
-
# a FormBuilder object associated with a particular model object to a block,
-
# and within the block allows methods to be called on the builder to
-
# generate fields associated with the model object. Fields may reflect
-
# a model object in two ways - how they are named (hence how submitted
-
# values appear within the +params+ hash in the controller) and what
-
# default values are shown when the form the fields appear in is first
-
# displayed. In order for both of these features to be specified independently,
-
# both an object name (represented by either a symbol or string) and the
-
# object itself can be passed to the method separately -
-
#
-
# <%= form_for @person do |person_form| %>
-
# First name: <%= person_form.text_field :first_name %>
-
# Last name : <%= person_form.text_field :last_name %>
-
#
-
# <%= fields_for :permission, @person.permission do |permission_fields| %>
-
# Admin? : <%= permission_fields.check_box :admin %>
-
# <% end %>
-
#
-
# <%= f.submit %>
-
# <% end %>
-
#
-
# In this case, the checkbox field will be represented by an HTML +input+
-
# tag with the +name+ attribute <tt>permission[admin]</tt>, and the submitted
-
# value will appear in the controller as <tt>params[:permission][:admin]</tt>.
-
# If <tt>@person.permission</tt> is an existing record with an attribute
-
# +admin+, the initial state of the checkbox when first displayed will
-
# reflect the value of <tt>@person.permission.admin</tt>.
-
#
-
# Often this can be simplified by passing just the name of the model
-
# object to +fields_for+ -
-
#
-
# <%= fields_for :permission do |permission_fields| %>
-
# Admin?: <%= permission_fields.check_box :admin %>
-
# <% end %>
-
#
-
# ...in which case, if <tt>:permission</tt> also happens to be the name of an
-
# instance variable <tt>@permission</tt>, the initial state of the input
-
# field will reflect the value of that variable's attribute <tt>@permission.admin</tt>.
-
#
-
# Alternatively, you can pass just the model object itself (if the first
-
# argument isn't a string or symbol +fields_for+ will realize that the
-
# name has been omitted) -
-
#
-
# <%= fields_for @person.permission do |permission_fields| %>
-
# Admin?: <%= permission_fields.check_box :admin %>
-
# <% end %>
-
#
-
# and +fields_for+ will derive the required name of the field from the
-
# _class_ of the model object, e.g. if <tt>@person.permission</tt>, is
-
# of class +Permission+, the field will still be named <tt>permission[admin]</tt>.
-
#
-
# Note: This also works for the methods in FormOptionHelper and
-
# DateHelper that are designed to work with an object as base, like
-
# FormOptionHelper#collection_select and DateHelper#datetime_select.
-
#
-
# === Nested Attributes Examples
-
#
-
# When the object belonging to the current scope has a nested attribute
-
# writer for a certain attribute, fields_for will yield a new scope
-
# for that attribute. This allows you to create forms that set or change
-
# the attributes of a parent object and its associations in one go.
-
#
-
# Nested attribute writers are normal setter methods named after an
-
# association. The most common way of defining these writers is either
-
# with +accepts_nested_attributes_for+ in a model definition or by
-
# defining a method with the proper name. For example: the attribute
-
# writer for the association <tt>:address</tt> is called
-
# <tt>address_attributes=</tt>.
-
#
-
# Whether a one-to-one or one-to-many style form builder will be yielded
-
# depends on whether the normal reader method returns a _single_ object
-
# or an _array_ of objects.
-
#
-
# ==== One-to-one
-
#
-
# Consider a Person class which returns a _single_ Address from the
-
# <tt>address</tt> reader method and responds to the
-
# <tt>address_attributes=</tt> writer method:
-
#
-
# class Person
-
# def address
-
# @address
-
# end
-
#
-
# def address_attributes=(attributes)
-
# # Process the attributes hash
-
# end
-
# end
-
#
-
# This model can now be used with a nested fields_for, like so:
-
#
-
# <%= form_for @person do |person_form| %>
-
# ...
-
# <%= person_form.fields_for :address do |address_fields| %>
-
# Street : <%= address_fields.text_field :street %>
-
# Zip code: <%= address_fields.text_field :zip_code %>
-
# <% end %>
-
# ...
-
# <% end %>
-
#
-
# When address is already an association on a Person you can use
-
# +accepts_nested_attributes_for+ to define the writer method for you:
-
#
-
# class Person < ActiveRecord::Base
-
# has_one :address
-
# accepts_nested_attributes_for :address
-
# end
-
#
-
# If you want to destroy the associated model through the form, you have
-
# to enable it first using the <tt>:allow_destroy</tt> option for
-
# +accepts_nested_attributes_for+:
-
#
-
# class Person < ActiveRecord::Base
-
# has_one :address
-
# accepts_nested_attributes_for :address, allow_destroy: true
-
# end
-
#
-
# Now, when you use a form element with the <tt>_destroy</tt> parameter,
-
# with a value that evaluates to +true+, you will destroy the associated
-
# model (eg. 1, '1', true, or 'true'):
-
#
-
# <%= form_for @person do |person_form| %>
-
# ...
-
# <%= person_form.fields_for :address do |address_fields| %>
-
# ...
-
# Delete: <%= address_fields.check_box :_destroy %>
-
# <% end %>
-
# ...
-
# <% end %>
-
#
-
# ==== One-to-many
-
#
-
# Consider a Person class which returns an _array_ of Project instances
-
# from the <tt>projects</tt> reader method and responds to the
-
# <tt>projects_attributes=</tt> writer method:
-
#
-
# class Person
-
# def projects
-
# [@project1, @project2]
-
# end
-
#
-
# def projects_attributes=(attributes)
-
# # Process the attributes hash
-
# end
-
# end
-
#
-
# Note that the <tt>projects_attributes=</tt> writer method is in fact
-
# required for fields_for to correctly identify <tt>:projects</tt> as a
-
# collection, and the correct indices to be set in the form markup.
-
#
-
# When projects is already an association on Person you can use
-
# +accepts_nested_attributes_for+ to define the writer method for you:
-
#
-
# class Person < ActiveRecord::Base
-
# has_many :projects
-
# accepts_nested_attributes_for :projects
-
# end
-
#
-
# This model can now be used with a nested fields_for. The block given to
-
# the nested fields_for call will be repeated for each instance in the
-
# collection:
-
#
-
# <%= form_for @person do |person_form| %>
-
# ...
-
# <%= person_form.fields_for :projects do |project_fields| %>
-
# <% if project_fields.object.active? %>
-
# Name: <%= project_fields.text_field :name %>
-
# <% end %>
-
# <% end %>
-
# ...
-
# <% end %>
-
#
-
# It's also possible to specify the instance to be used:
-
#
-
# <%= form_for @person do |person_form| %>
-
# ...
-
# <% @person.projects.each do |project| %>
-
# <% if project.active? %>
-
# <%= person_form.fields_for :projects, project do |project_fields| %>
-
# Name: <%= project_fields.text_field :name %>
-
# <% end %>
-
# <% end %>
-
# <% end %>
-
# ...
-
# <% end %>
-
#
-
# Or a collection to be used:
-
#
-
# <%= form_for @person do |person_form| %>
-
# ...
-
# <%= person_form.fields_for :projects, @active_projects do |project_fields| %>
-
# Name: <%= project_fields.text_field :name %>
-
# <% end %>
-
# ...
-
# <% end %>
-
#
-
# When projects is already an association on Person you can use
-
# +accepts_nested_attributes_for+ to define the writer method for you:
-
#
-
# class Person < ActiveRecord::Base
-
# has_many :projects
-
# accepts_nested_attributes_for :projects
-
# end
-
#
-
# If you want to destroy any of the associated models through the
-
# form, you have to enable it first using the <tt>:allow_destroy</tt>
-
# option for +accepts_nested_attributes_for+:
-
#
-
# class Person < ActiveRecord::Base
-
# has_many :projects
-
# accepts_nested_attributes_for :projects, allow_destroy: true
-
# end
-
#
-
# This will allow you to specify which models to destroy in the
-
# attributes hash by adding a form element for the <tt>_destroy</tt>
-
# parameter with a value that evaluates to +true+
-
# (eg. 1, '1', true, or 'true'):
-
#
-
# <%= form_for @person do |person_form| %>
-
# ...
-
# <%= person_form.fields_for :projects do |project_fields| %>
-
# Delete: <%= project_fields.check_box :_destroy %>
-
# <% end %>
-
# ...
-
# <% end %>
-
#
-
# When a collection is used you might want to know the index of each
-
# object into the array. For this purpose, the <tt>index</tt> method
-
# is available in the FormBuilder object.
-
#
-
# <%= form_for @person do |person_form| %>
-
# ...
-
# <%= person_form.fields_for :projects do |project_fields| %>
-
# Project #<%= project_fields.index %>
-
# ...
-
# <% end %>
-
# ...
-
# <% end %>
-
1
def fields_for(record_name, record_object = nil, options = {}, &block)
-
builder = instantiate_builder(record_name, record_object, options)
-
output = capture(builder, &block)
-
output.concat builder.hidden_field(:id) if output && options[:hidden_field_id] && !builder.emitted_hidden_id?
-
output
-
end
-
-
# Returns a label tag tailored for labelling an input field for a specified attribute (identified by +method+) on an object
-
# assigned to the template (identified by +object+). The text of label will default to the attribute name unless a translation
-
# is found in the current I18n locale (through helpers.label.<modelname>.<attribute>) or you specify it explicitly.
-
# Additional options on the label tag can be passed as a hash with +options+. These options will be tagged
-
# onto the HTML as an HTML element attribute as in the example shown, except for the <tt>:value</tt> option, which is designed to
-
# target labels for radio_button tags (where the value is used in the ID of the input tag).
-
#
-
# ==== Examples
-
# label(:post, :title)
-
# # => <label for="post_title">Title</label>
-
#
-
# You can localize your labels based on model and attribute names.
-
# For example you can define the following in your locale (e.g. en.yml)
-
#
-
# helpers:
-
# label:
-
# post:
-
# body: "Write your entire text here"
-
#
-
# Which then will result in
-
#
-
# label(:post, :body)
-
# # => <label for="post_body">Write your entire text here</label>
-
#
-
# Localization can also be based purely on the translation of the attribute-name
-
# (if you are using ActiveRecord):
-
#
-
# activerecord:
-
# attributes:
-
# post:
-
# cost: "Total cost"
-
#
-
# label(:post, :cost)
-
# # => <label for="post_cost">Total cost</label>
-
#
-
# label(:post, :title, "A short title")
-
# # => <label for="post_title">A short title</label>
-
#
-
# label(:post, :title, "A short title", class: "title_label")
-
# # => <label for="post_title" class="title_label">A short title</label>
-
#
-
# label(:post, :privacy, "Public Post", value: "public")
-
# # => <label for="post_privacy_public">Public Post</label>
-
#
-
# label(:post, :terms) do
-
# 'Accept <a href="/terms">Terms</a>.'.html_safe
-
# end
-
1
def label(object_name, method, content_or_options = nil, options = nil, &block)
-
Tags::Label.new(object_name, method, self, content_or_options, options).render(&block)
-
end
-
-
# Returns an input tag of the "text" type tailored for accessing a specified attribute (identified by +method+) on an object
-
# assigned to the template (identified by +object+). Additional options on the input tag can be passed as a
-
# hash with +options+. These options will be tagged onto the HTML as an HTML element attribute as in the example
-
# shown.
-
#
-
# ==== Examples
-
# text_field(:post, :title, size: 20)
-
# # => <input type="text" id="post_title" name="post[title]" size="20" value="#{@post.title}" />
-
#
-
# text_field(:post, :title, class: "create_input")
-
# # => <input type="text" id="post_title" name="post[title]" value="#{@post.title}" class="create_input" />
-
#
-
# text_field(:session, :user, onchange: "if $('session[user]').value == 'admin' { alert('Your login can not be admin!'); }")
-
# # => <input type="text" id="session_user" name="session[user]" value="#{@session.user}" onchange = "if $('session[user]').value == 'admin' { alert('Your login can not be admin!'); }"/>
-
#
-
# text_field(:snippet, :code, size: 20, class: 'code_input')
-
# # => <input type="text" id="snippet_code" name="snippet[code]" size="20" value="#{@snippet.code}" class="code_input" />
-
1
def text_field(object_name, method, options = {})
-
Tags::TextField.new(object_name, method, self, options).render
-
end
-
-
# Returns an input tag of the "password" type tailored for accessing a specified attribute (identified by +method+) on an object
-
# assigned to the template (identified by +object+). Additional options on the input tag can be passed as a
-
# hash with +options+. These options will be tagged onto the HTML as an HTML element attribute as in the example
-
# shown. For security reasons this field is blank by default; pass in a value via +options+ if this is not desired.
-
#
-
# ==== Examples
-
# password_field(:login, :pass, size: 20)
-
# # => <input type="password" id="login_pass" name="login[pass]" size="20" />
-
#
-
# password_field(:account, :secret, class: "form_input", value: @account.secret)
-
# # => <input type="password" id="account_secret" name="account[secret]" value="#{@account.secret}" class="form_input" />
-
#
-
# password_field(:user, :password, onchange: "if $('user[password]').length > 30 { alert('Your password needs to be shorter!'); }")
-
# # => <input type="password" id="user_password" name="user[password]" onchange = "if $('user[password]').length > 30 { alert('Your password needs to be shorter!'); }"/>
-
#
-
# password_field(:account, :pin, size: 20, class: 'form_input')
-
# # => <input type="password" id="account_pin" name="account[pin]" size="20" class="form_input" />
-
1
def password_field(object_name, method, options = {})
-
Tags::PasswordField.new(object_name, method, self, options).render
-
end
-
-
# Returns a hidden input tag tailored for accessing a specified attribute (identified by +method+) on an object
-
# assigned to the template (identified by +object+). Additional options on the input tag can be passed as a
-
# hash with +options+. These options will be tagged onto the HTML as an HTML element attribute as in the example
-
# shown.
-
#
-
# ==== Examples
-
# hidden_field(:signup, :pass_confirm)
-
# # => <input type="hidden" id="signup_pass_confirm" name="signup[pass_confirm]" value="#{@signup.pass_confirm}" />
-
#
-
# hidden_field(:post, :tag_list)
-
# # => <input type="hidden" id="post_tag_list" name="post[tag_list]" value="#{@post.tag_list}" />
-
#
-
# hidden_field(:user, :token)
-
# # => <input type="hidden" id="user_token" name="user[token]" value="#{@user.token}" />
-
1
def hidden_field(object_name, method, options = {})
-
Tags::HiddenField.new(object_name, method, self, options).render
-
end
-
-
# Returns a file upload input tag tailored for accessing a specified attribute (identified by +method+) on an object
-
# assigned to the template (identified by +object+). Additional options on the input tag can be passed as a
-
# hash with +options+. These options will be tagged onto the HTML as an HTML element attribute as in the example
-
# shown.
-
#
-
# Using this method inside a +form_for+ block will set the enclosing form's encoding to <tt>multipart/form-data</tt>.
-
#
-
# ==== Examples
-
# file_field(:user, :avatar)
-
# # => <input type="file" id="user_avatar" name="user[avatar]" />
-
#
-
# file_field(:post, :attached, accept: 'text/html')
-
# # => <input accept="text/html" type="file" id="post_attached" name="post[attached]" />
-
#
-
# file_field(:attachment, :file, class: 'file_input')
-
# # => <input type="file" id="attachment_file" name="attachment[file]" class="file_input" />
-
1
def file_field(object_name, method, options = {})
-
Tags::FileField.new(object_name, method, self, options).render
-
end
-
-
# Returns a textarea opening and closing tag set tailored for accessing a specified attribute (identified by +method+)
-
# on an object assigned to the template (identified by +object+). Additional options on the input tag can be passed as a
-
# hash with +options+.
-
#
-
# ==== Examples
-
# text_area(:post, :body, cols: 20, rows: 40)
-
# # => <textarea cols="20" rows="40" id="post_body" name="post[body]">
-
# # #{@post.body}
-
# # </textarea>
-
#
-
# text_area(:comment, :text, size: "20x30")
-
# # => <textarea cols="20" rows="30" id="comment_text" name="comment[text]">
-
# # #{@comment.text}
-
# # </textarea>
-
#
-
# text_area(:application, :notes, cols: 40, rows: 15, class: 'app_input')
-
# # => <textarea cols="40" rows="15" id="application_notes" name="application[notes]" class="app_input">
-
# # #{@application.notes}
-
# # </textarea>
-
#
-
# text_area(:entry, :body, size: "20x20", disabled: 'disabled')
-
# # => <textarea cols="20" rows="20" id="entry_body" name="entry[body]" disabled="disabled">
-
# # #{@entry.body}
-
# # </textarea>
-
1
def text_area(object_name, method, options = {})
-
Tags::TextArea.new(object_name, method, self, options).render
-
end
-
-
# Returns a checkbox tag tailored for accessing a specified attribute (identified by +method+) on an object
-
# assigned to the template (identified by +object+). This object must be an instance object (@object) and not a local object.
-
# It's intended that +method+ returns an integer and if that integer is above zero, then the checkbox is checked.
-
# Additional options on the input tag can be passed as a hash with +options+. The +checked_value+ defaults to 1
-
# while the default +unchecked_value+ is set to 0 which is convenient for boolean values.
-
#
-
# ==== Gotcha
-
#
-
# The HTML specification says unchecked check boxes are not successful, and
-
# thus web browsers do not send them. Unfortunately this introduces a gotcha:
-
# if an +Invoice+ model has a +paid+ flag, and in the form that edits a paid
-
# invoice the user unchecks its check box, no +paid+ parameter is sent. So,
-
# any mass-assignment idiom like
-
#
-
# @invoice.update_attributes(params[:invoice])
-
#
-
# wouldn't update the flag.
-
#
-
# To prevent this the helper generates an auxiliary hidden field before
-
# the very check box. The hidden field has the same name and its
-
# attributes mimic an unchecked check box.
-
#
-
# This way, the client either sends only the hidden field (representing
-
# the check box is unchecked), or both fields. Since the HTML specification
-
# says key/value pairs have to be sent in the same order they appear in the
-
# form, and parameters extraction gets the last occurrence of any repeated
-
# key in the query string, that works for ordinary forms.
-
#
-
# Unfortunately that workaround does not work when the check box goes
-
# within an array-like parameter, as in
-
#
-
# <%= fields_for "project[invoice_attributes][]", invoice, index: nil do |form| %>
-
# <%= form.check_box :paid %>
-
# ...
-
# <% end %>
-
#
-
# because parameter name repetition is precisely what Rails seeks to distinguish
-
# the elements of the array. For each item with a checked check box you
-
# get an extra ghost item with only that attribute, assigned to "0".
-
#
-
# In that case it is preferable to either use +check_box_tag+ or to use
-
# hashes instead of arrays.
-
#
-
# # Let's say that @post.validated? is 1:
-
# check_box("post", "validated")
-
# # => <input name="post[validated]" type="hidden" value="0" />
-
# # <input checked="checked" type="checkbox" id="post_validated" name="post[validated]" value="1" />
-
#
-
# # Let's say that @puppy.gooddog is "no":
-
# check_box("puppy", "gooddog", {}, "yes", "no")
-
# # => <input name="puppy[gooddog]" type="hidden" value="no" />
-
# # <input type="checkbox" id="puppy_gooddog" name="puppy[gooddog]" value="yes" />
-
#
-
# check_box("eula", "accepted", { class: 'eula_check' }, "yes", "no")
-
# # => <input name="eula[accepted]" type="hidden" value="no" />
-
# # <input type="checkbox" class="eula_check" id="eula_accepted" name="eula[accepted]" value="yes" />
-
1
def check_box(object_name, method, options = {}, checked_value = "1", unchecked_value = "0")
-
Tags::CheckBox.new(object_name, method, self, checked_value, unchecked_value, options).render
-
end
-
-
# Returns a radio button tag for accessing a specified attribute (identified by +method+) on an object
-
# assigned to the template (identified by +object+). If the current value of +method+ is +tag_value+ the
-
# radio button will be checked.
-
#
-
# To force the radio button to be checked pass <tt>checked: true</tt> in the
-
# +options+ hash. You may pass HTML options there as well.
-
#
-
# # Let's say that @post.category returns "rails":
-
# radio_button("post", "category", "rails")
-
# radio_button("post", "category", "java")
-
# # => <input type="radio" id="post_category_rails" name="post[category]" value="rails" checked="checked" />
-
# # <input type="radio" id="post_category_java" name="post[category]" value="java" />
-
#
-
# radio_button("user", "receive_newsletter", "yes")
-
# radio_button("user", "receive_newsletter", "no")
-
# # => <input type="radio" id="user_receive_newsletter_yes" name="user[receive_newsletter]" value="yes" />
-
# # <input type="radio" id="user_receive_newsletter_no" name="user[receive_newsletter]" value="no" checked="checked" />
-
1
def radio_button(object_name, method, tag_value, options = {})
-
Tags::RadioButton.new(object_name, method, self, tag_value, options).render
-
end
-
-
# Returns a text_field of type "color".
-
#
-
# color_field("car", "color")
-
# # => <input id="car_color" name="car[color]" type="color" value="#000000" />
-
1
def color_field(object_name, method, options = {})
-
Tags::ColorField.new(object_name, method, self, options).render
-
end
-
-
# Returns an input of type "search" for accessing a specified attribute (identified by +method+) on an object
-
# assigned to the template (identified by +object_name+). Inputs of type "search" may be styled differently by
-
# some browsers.
-
#
-
# search_field(:user, :name)
-
# # => <input id="user_name" name="user[name]" type="search" />
-
# search_field(:user, :name, autosave: false)
-
# # => <input autosave="false" id="user_name" name="user[name]" type="search" />
-
# search_field(:user, :name, results: 3)
-
# # => <input id="user_name" name="user[name]" results="3" type="search" />
-
# # Assume request.host returns "www.example.com"
-
# search_field(:user, :name, autosave: true)
-
# # => <input autosave="com.example.www" id="user_name" name="user[name]" results="10" type="search" />
-
# search_field(:user, :name, onsearch: true)
-
# # => <input id="user_name" incremental="true" name="user[name]" onsearch="true" type="search" />
-
# search_field(:user, :name, autosave: false, onsearch: true)
-
# # => <input autosave="false" id="user_name" incremental="true" name="user[name]" onsearch="true" type="search" />
-
# search_field(:user, :name, autosave: true, onsearch: true)
-
# # => <input autosave="com.example.www" id="user_name" incremental="true" name="user[name]" onsearch="true" results="10" type="search" />
-
1
def search_field(object_name, method, options = {})
-
Tags::SearchField.new(object_name, method, self, options).render
-
end
-
-
# Returns a text_field of type "tel".
-
#
-
# telephone_field("user", "phone")
-
# # => <input id="user_phone" name="user[phone]" type="tel" />
-
#
-
1
def telephone_field(object_name, method, options = {})
-
Tags::TelField.new(object_name, method, self, options).render
-
end
-
# aliases telephone_field
-
1
alias phone_field telephone_field
-
-
# Returns a text_field of type "date".
-
#
-
# date_field("user", "born_on")
-
# # => <input id="user_born_on" name="user[born_on]" type="date" />
-
#
-
# The default value is generated by trying to call "to_date"
-
# on the object's value, which makes it behave as expected for instances
-
# of DateTime and ActiveSupport::TimeWithZone. You can still override that
-
# by passing the "value" option explicitly, e.g.
-
#
-
# @user.born_on = Date.new(1984, 1, 27)
-
# date_field("user", "born_on", value: "1984-05-12")
-
# # => <input id="user_born_on" name="user[born_on]" type="date" value="1984-05-12" />
-
#
-
1
def date_field(object_name, method, options = {})
-
Tags::DateField.new(object_name, method, self, options).render
-
end
-
-
# Returns a text_field of type "time".
-
#
-
# The default value is generated by trying to call +strftime+ with "%T.%L"
-
# on the objects's value. It is still possible to override that
-
# by passing the "value" option.
-
#
-
# === Options
-
# * Accepts same options as time_field_tag
-
#
-
# === Example
-
# time_field("task", "started_at")
-
# # => <input id="task_started_at" name="task[started_at]" type="time" />
-
#
-
1
def time_field(object_name, method, options = {})
-
Tags::TimeField.new(object_name, method, self, options).render
-
end
-
-
# Returns a text_field of type "datetime".
-
#
-
# datetime_field("user", "born_on")
-
# # => <input id="user_born_on" name="user[born_on]" type="datetime" />
-
#
-
# The default value is generated by trying to call +strftime+ with "%Y-%m-%dT%T.%L%z"
-
# on the object's value, which makes it behave as expected for instances
-
# of DateTime and ActiveSupport::TimeWithZone.
-
#
-
# @user.born_on = Date.new(1984, 1, 12)
-
# datetime_field("user", "born_on")
-
# # => <input id="user_born_on" name="user[born_on]" type="datetime" value="1984-01-12T00:00:00.000+0000" />
-
#
-
1
def datetime_field(object_name, method, options = {})
-
Tags::DatetimeField.new(object_name, method, self, options).render
-
end
-
-
# Returns a text_field of type "datetime-local".
-
#
-
# datetime_local_field("user", "born_on")
-
# # => <input id="user_born_on" name="user[born_on]" type="datetime-local" />
-
#
-
# The default value is generated by trying to call +strftime+ with "%Y-%m-%dT%T"
-
# on the object's value, which makes it behave as expected for instances
-
# of DateTime and ActiveSupport::TimeWithZone.
-
#
-
# @user.born_on = Date.new(1984, 1, 12)
-
# datetime_local_field("user", "born_on")
-
# # => <input id="user_born_on" name="user[born_on]" type="datetime-local" value="1984-01-12T00:00:00" />
-
#
-
1
def datetime_local_field(object_name, method, options = {})
-
Tags::DatetimeLocalField.new(object_name, method, self, options).render
-
end
-
-
# Returns a text_field of type "month".
-
#
-
# month_field("user", "born_on")
-
# # => <input id="user_born_on" name="user[born_on]" type="month" />
-
#
-
# The default value is generated by trying to call +strftime+ with "%Y-%m"
-
# on the object's value, which makes it behave as expected for instances
-
# of DateTime and ActiveSupport::TimeWithZone.
-
#
-
# @user.born_on = Date.new(1984, 1, 27)
-
# month_field("user", "born_on")
-
# # => <input id="user_born_on" name="user[born_on]" type="date" value="1984-01" />
-
#
-
1
def month_field(object_name, method, options = {})
-
Tags::MonthField.new(object_name, method, self, options).render
-
end
-
-
# Returns a text_field of type "week".
-
#
-
# week_field("user", "born_on")
-
# # => <input id="user_born_on" name="user[born_on]" type="week" />
-
#
-
# The default value is generated by trying to call +strftime+ with "%Y-W%W"
-
# on the object's value, which makes it behave as expected for instances
-
# of DateTime and ActiveSupport::TimeWithZone.
-
#
-
# @user.born_on = Date.new(1984, 5, 12)
-
# week_field("user", "born_on")
-
# # => <input id="user_born_on" name="user[born_on]" type="date" value="1984-W19" />
-
#
-
1
def week_field(object_name, method, options = {})
-
Tags::WeekField.new(object_name, method, self, options).render
-
end
-
-
# Returns a text_field of type "url".
-
#
-
# url_field("user", "homepage")
-
# # => <input id="user_homepage" name="user[homepage]" type="url" />
-
#
-
1
def url_field(object_name, method, options = {})
-
Tags::UrlField.new(object_name, method, self, options).render
-
end
-
-
# Returns a text_field of type "email".
-
#
-
# email_field("user", "address")
-
# # => <input id="user_address" name="user[address]" type="email" />
-
#
-
1
def email_field(object_name, method, options = {})
-
Tags::EmailField.new(object_name, method, self, options).render
-
end
-
-
# Returns an input tag of type "number".
-
#
-
# ==== Options
-
# * Accepts same options as number_field_tag
-
1
def number_field(object_name, method, options = {})
-
Tags::NumberField.new(object_name, method, self, options).render
-
end
-
-
# Returns an input tag of type "range".
-
#
-
# ==== Options
-
# * Accepts same options as range_field_tag
-
1
def range_field(object_name, method, options = {})
-
Tags::RangeField.new(object_name, method, self, options).render
-
end
-
-
1
private
-
-
1
def instantiate_builder(record_name, record_object, options)
-
case record_name
-
when String, Symbol
-
object = record_object
-
object_name = record_name
-
else
-
object = record_name
-
object_name = model_name_from_record_or_class(object).param_key
-
end
-
-
builder = options[:builder] || default_form_builder
-
builder.new(object_name, object, self, options)
-
end
-
-
1
def default_form_builder
-
builder = ActionView::Base.default_form_builder
-
builder.respond_to?(:constantize) ? builder.constantize : builder
-
end
-
end
-
-
1
class FormBuilder
-
1
include ModelNaming
-
-
# The methods which wrap a form helper call.
-
1
class_attribute :field_helpers
-
1
self.field_helpers = FormHelper.instance_methods - [:form_for, :convert_to_model, :model_name_from_record_or_class]
-
-
1
attr_accessor :object_name, :object, :options
-
-
1
attr_reader :multipart, :parent_builder, :index
-
1
alias :multipart? :multipart
-
-
1
def multipart=(multipart)
-
@multipart = multipart
-
parent_builder.multipart = multipart if parent_builder
-
end
-
-
1
def self._to_partial_path
-
@_to_partial_path ||= name.demodulize.underscore.sub!(/_builder$/, '')
-
end
-
-
1
def to_partial_path
-
self.class._to_partial_path
-
end
-
-
1
def to_model
-
self
-
end
-
-
1
def initialize(object_name, object, template, options, block=nil)
-
if block
-
ActiveSupport::Deprecation.warn "Giving a block to FormBuilder is deprecated and has no effect anymore."
-
end
-
-
@nested_child_index = {}
-
@object_name, @object, @template, @options = object_name, object, template, options
-
@parent_builder = options[:parent_builder]
-
@default_options = @options ? @options.slice(:index, :namespace) : {}
-
if @object_name.to_s.match(/\[\]$/)
-
if object ||= @template.instance_variable_get("@#{Regexp.last_match.pre_match}") and object.respond_to?(:to_param)
-
@auto_index = object.to_param
-
else
-
raise ArgumentError, "object[] naming but object param and @object var don't exist or don't respond to to_param: #{object.inspect}"
-
end
-
end
-
@multipart = nil
-
@index = options[:index] || options[:child_index]
-
end
-
-
1
(field_helpers - [:label, :check_box, :radio_button, :fields_for, :hidden_field, :file_field]).each do |selector|
-
17
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
-
def #{selector}(method, options = {}) # def text_field(method, options = {})
-
@template.send( # @template.send(
-
#{selector.inspect}, # "text_field",
-
@object_name, # @object_name,
-
method, # method,
-
objectify_options(options)) # objectify_options(options))
-
end # end
-
RUBY_EVAL
-
end
-
-
1
def fields_for(record_name, record_object = nil, fields_options = {}, &block)
-
fields_options, record_object = record_object, nil if record_object.is_a?(Hash) && record_object.extractable_options?
-
fields_options[:builder] ||= options[:builder]
-
fields_options[:parent_builder] = self
-
fields_options[:namespace] = options[:namespace]
-
-
case record_name
-
when String, Symbol
-
if nested_attributes_association?(record_name)
-
return fields_for_with_nested_attributes(record_name, record_object, fields_options, block)
-
end
-
else
-
record_object = record_name.is_a?(Array) ? record_name.last : record_name
-
record_name = model_name_from_record_or_class(record_object).param_key
-
end
-
-
index = if options.has_key?(:index)
-
options[:index]
-
elsif defined?(@auto_index)
-
self.object_name = @object_name.to_s.sub(/\[\]$/,"")
-
@auto_index
-
end
-
-
record_name = index ? "#{object_name}[#{index}][#{record_name}]" : "#{object_name}[#{record_name}]"
-
fields_options[:child_index] = index
-
-
@template.fields_for(record_name, record_object, fields_options, &block)
-
end
-
-
1
def label(method, text = nil, options = {}, &block)
-
@template.label(@object_name, method, text, objectify_options(options), &block)
-
end
-
-
1
def check_box(method, options = {}, checked_value = "1", unchecked_value = "0")
-
@template.check_box(@object_name, method, objectify_options(options), checked_value, unchecked_value)
-
end
-
-
1
def radio_button(method, tag_value, options = {})
-
@template.radio_button(@object_name, method, tag_value, objectify_options(options))
-
end
-
-
1
def hidden_field(method, options = {})
-
@emitted_hidden_id = true if method == :id
-
@template.hidden_field(@object_name, method, objectify_options(options))
-
end
-
-
1
def file_field(method, options = {})
-
self.multipart = true
-
@template.file_field(@object_name, method, objectify_options(options))
-
end
-
-
# Add the submit button for the given form. When no value is given, it checks
-
# if the object is a new resource or not to create the proper label:
-
#
-
# <%= form_for @post do |f| %>
-
# <%= f.submit %>
-
# <% end %>
-
#
-
# In the example above, if @post is a new record, it will use "Create Post" as
-
# submit button label, otherwise, it uses "Update Post".
-
#
-
# Those labels can be customized using I18n, under the helpers.submit key and accept
-
# the %{model} as translation interpolation:
-
#
-
# en:
-
# helpers:
-
# submit:
-
# create: "Create a %{model}"
-
# update: "Confirm changes to %{model}"
-
#
-
# It also searches for a key specific for the given object:
-
#
-
# en:
-
# helpers:
-
# submit:
-
# post:
-
# create: "Add %{model}"
-
#
-
1
def submit(value=nil, options={})
-
value, options = nil, value if value.is_a?(Hash)
-
value ||= submit_default_value
-
@template.submit_tag(value, options)
-
end
-
-
# Add the submit button for the given form. When no value is given, it checks
-
# if the object is a new resource or not to create the proper label:
-
#
-
# <%= form_for @post do |f| %>
-
# <%= f.button %>
-
# <% end %>
-
#
-
# In the example above, if @post is a new record, it will use "Create Post" as
-
# button label, otherwise, it uses "Update Post".
-
#
-
# Those labels can be customized using I18n, under the helpers.submit key
-
# (the same as submit helper) and accept the %{model} as translation interpolation:
-
#
-
# en:
-
# helpers:
-
# submit:
-
# create: "Create a %{model}"
-
# update: "Confirm changes to %{model}"
-
#
-
# It also searches for a key specific for the given object:
-
#
-
# en:
-
# helpers:
-
# submit:
-
# post:
-
# create: "Add %{model}"
-
#
-
# ==== Examples
-
# button("Create a post")
-
# # => <button name='button' type='submit'>Create post</button>
-
#
-
# button do
-
# content_tag(:strong, 'Ask me!')
-
# end
-
# # => <button name='button' type='submit'>
-
# # <strong>Ask me!</strong>
-
# # </button>
-
#
-
1
def button(value = nil, options = {}, &block)
-
value, options = nil, value if value.is_a?(Hash)
-
value ||= submit_default_value
-
@template.button_tag(value, options, &block)
-
end
-
-
1
def emitted_hidden_id?
-
@emitted_hidden_id ||= nil
-
end
-
-
1
private
-
1
def objectify_options(options)
-
@default_options.merge(options.merge(object: @object))
-
end
-
-
1
def submit_default_value
-
object = convert_to_model(@object)
-
key = object ? (object.persisted? ? :update : :create) : :submit
-
-
model = if object.class.respond_to?(:model_name)
-
object.class.model_name.human
-
else
-
@object_name.to_s.humanize
-
end
-
-
defaults = []
-
defaults << :"helpers.submit.#{object_name}.#{key}"
-
defaults << :"helpers.submit.#{key}"
-
defaults << "#{key.to_s.humanize} #{model}"
-
-
I18n.t(defaults.shift, model: model, default: defaults)
-
end
-
-
1
def nested_attributes_association?(association_name)
-
@object.respond_to?("#{association_name}_attributes=")
-
end
-
-
1
def fields_for_with_nested_attributes(association_name, association, options, block)
-
name = "#{object_name}[#{association_name}_attributes]"
-
association = convert_to_model(association)
-
-
if association.respond_to?(:persisted?)
-
association = [association] if @object.send(association_name).is_a?(Array)
-
elsif !association.respond_to?(:to_ary)
-
association = @object.send(association_name)
-
end
-
-
if association.respond_to?(:to_ary)
-
explicit_child_index = options[:child_index]
-
output = ActiveSupport::SafeBuffer.new
-
association.each do |child|
-
options[:child_index] = nested_child_index(name) unless explicit_child_index
-
output << fields_for_nested_model("#{name}[#{options[:child_index]}]", child, options, block)
-
end
-
output
-
elsif association
-
fields_for_nested_model(name, association, options, block)
-
end
-
end
-
-
1
def fields_for_nested_model(name, object, options, block)
-
object = convert_to_model(object)
-
-
parent_include_id = self.options.fetch(:include_id, true)
-
include_id = options.fetch(:include_id, parent_include_id)
-
options[:hidden_field_id] = object.persisted? && include_id
-
@template.fields_for(name, object, options, &block)
-
end
-
-
1
def nested_child_index(name)
-
@nested_child_index[name] ||= -1
-
@nested_child_index[name] += 1
-
end
-
end
-
end
-
-
1
ActiveSupport.on_load(:action_view) do
-
2
cattr_accessor(:default_form_builder) { ::ActionView::Helpers::FormBuilder }
-
end
-
end
-
1
require 'cgi'
-
1
require 'erb'
-
1
require 'action_view/helpers/form_helper'
-
1
require 'active_support/core_ext/string/output_safety'
-
-
1
module ActionView
-
# = Action View Form Option Helpers
-
1
module Helpers
-
# Provides a number of methods for turning different kinds of containers into a set of option tags.
-
# == Options
-
# The <tt>collection_select</tt>, <tt>select</tt> and <tt>time_zone_select</tt> methods take an <tt>options</tt> parameter, a hash:
-
#
-
# * <tt>:include_blank</tt> - set to true or a prompt string if the first option element of the select element is a blank. Useful if there is not a default value required for the select element.
-
#
-
# For example,
-
#
-
# select("post", "category", Post::CATEGORIES, {include_blank: true})
-
#
-
# could become:
-
#
-
# <select name="post[category]">
-
# <option></option>
-
# <option>joke</option>
-
# <option>poem</option>
-
# </select>
-
#
-
# Another common case is a select tag for an <tt>belongs_to</tt>-associated object.
-
#
-
# Example with @post.person_id => 2:
-
#
-
# select("post", "person_id", Person.all.collect {|p| [ p.name, p.id ] }, {include_blank: 'None'})
-
#
-
# could become:
-
#
-
# <select name="post[person_id]">
-
# <option value="">None</option>
-
# <option value="1">David</option>
-
# <option value="2" selected="selected">Sam</option>
-
# <option value="3">Tobias</option>
-
# </select>
-
#
-
# * <tt>:prompt</tt> - set to true or a prompt string. When the select element doesn't have a value yet, this prepends an option with a generic prompt -- "Please select" -- or the given prompt string.
-
#
-
# Example:
-
#
-
# select("post", "person_id", Person.all.collect {|p| [ p.name, p.id ] }, {prompt: 'Select Person'})
-
#
-
# could become:
-
#
-
# <select name="post[person_id]">
-
# <option value="">Select Person</option>
-
# <option value="1">David</option>
-
# <option value="2">Sam</option>
-
# <option value="3">Tobias</option>
-
# </select>
-
#
-
# Like the other form helpers, +select+ can accept an <tt>:index</tt> option to manually set the ID used in the resulting output. Unlike other helpers, +select+ expects this
-
# option to be in the +html_options+ parameter.
-
#
-
# Example:
-
#
-
# select("album[]", "genre", %w[rap rock country], {}, { index: nil })
-
#
-
# becomes:
-
#
-
# <select name="album[][genre]" id="album__genre">
-
# <option value="rap">rap</option>
-
# <option value="rock">rock</option>
-
# <option value="country">country</option>
-
# </select>
-
#
-
# * <tt>:disabled</tt> - can be a single value or an array of values that will be disabled options in the final output.
-
#
-
# Example:
-
#
-
# select("post", "category", Post::CATEGORIES, {disabled: 'restricted'})
-
#
-
# could become:
-
#
-
# <select name="post[category]">
-
# <option></option>
-
# <option>joke</option>
-
# <option>poem</option>
-
# <option disabled="disabled">restricted</option>
-
# </select>
-
#
-
# When used with the <tt>collection_select</tt> helper, <tt>:disabled</tt> can also be a Proc that identifies those options that should be disabled.
-
#
-
# Example:
-
#
-
# collection_select(:post, :category_id, Category.all, :id, :name, {disabled: lambda{|category| category.archived? }})
-
#
-
# If the categories "2008 stuff" and "Christmas" return true when the method <tt>archived?</tt> is called, this would return:
-
# <select name="post[category_id]">
-
# <option value="1" disabled="disabled">2008 stuff</option>
-
# <option value="2" disabled="disabled">Christmas</option>
-
# <option value="3">Jokes</option>
-
# <option value="4">Poems</option>
-
# </select>
-
#
-
1
module FormOptionsHelper
-
# ERB::Util can mask some helpers like textilize. Make sure to include them.
-
1
include TextHelper
-
-
# Create a select tag and a series of contained option tags for the provided object and method.
-
# The option currently held by the object will be selected, provided that the object is available.
-
#
-
# There are two possible formats for the choices parameter, corresponding to other helpers' output:
-
# * A flat collection: see options_for_select
-
# * A nested collection: see grouped_options_for_select
-
#
-
# Example with @post.person_id => 1:
-
# select("post", "person_id", Person.all.collect {|p| [ p.name, p.id ] }, { include_blank: true })
-
#
-
# could become:
-
#
-
# <select name="post[person_id]">
-
# <option value=""></option>
-
# <option value="1" selected="selected">David</option>
-
# <option value="2">Sam</option>
-
# <option value="3">Tobias</option>
-
# </select>
-
#
-
# This can be used to provide a default set of options in the standard way: before rendering the create form, a
-
# new model instance is assigned the default options and bound to @model_name. Usually this model is not saved
-
# to the database. Instead, a second model object is created when the create request is received.
-
# This allows the user to submit a form page more than once with the expected results of creating multiple records.
-
# In addition, this allows a single partial to be used to generate form inputs for both edit and create forms.
-
#
-
# By default, <tt>post.person_id</tt> is the selected option. Specify <tt>selected: value</tt> to use a different selection
-
# or <tt>selected: nil</tt> to leave all options unselected. Similarly, you can specify values to be disabled in the option
-
# tags by specifying the <tt>:disabled</tt> option. This can either be a single value or an array of values to be disabled.
-
#
-
# ==== Gotcha
-
#
-
# The HTML specification says when +multiple+ parameter passed to select and all options got deselected
-
# web browsers do not send any value to server. Unfortunately this introduces a gotcha:
-
# if an +User+ model has many +roles+ and have +role_ids+ accessor, and in the form that edits roles of the user
-
# the user deselects all roles from +role_ids+ multiple select box, no +role_ids+ parameter is sent. So,
-
# any mass-assignment idiom like
-
#
-
# @user.update_attributes(params[:user])
-
#
-
# wouldn't update roles.
-
#
-
# To prevent this the helper generates an auxiliary hidden field before
-
# every multiple select. The hidden field has the same name as multiple select and blank value.
-
#
-
# This way, the client either sends only the hidden field (representing
-
# the deselected multiple select box), or both fields. Since the HTML specification
-
# says key/value pairs have to be sent in the same order they appear in the
-
# form, and parameters extraction gets the last occurrence of any repeated
-
# key in the query string, that works for ordinary forms.
-
#
-
# In case if you don't want the helper to generate this hidden field you can specify <tt>include_hidden: false</tt> option.
-
#
-
1
def select(object, method, choices, options = {}, html_options = {})
-
Tags::Select.new(object, method, self, choices, options, html_options).render
-
end
-
-
# Returns <tt><select></tt> and <tt><option></tt> tags for the collection of existing return values of
-
# +method+ for +object+'s class. The value returned from calling +method+ on the instance +object+ will
-
# be selected. If calling +method+ returns +nil+, no selection is made without including <tt>:prompt</tt>
-
# or <tt>:include_blank</tt> in the +options+ hash.
-
#
-
# The <tt>:value_method</tt> and <tt>:text_method</tt> parameters are methods to be called on each member
-
# of +collection+. The return values are used as the +value+ attribute and contents of each
-
# <tt><option></tt> tag, respectively. They can also be any object that responds to +call+, such
-
# as a +proc+, that will be called for each member of the +collection+ to
-
# retrieve the value/text.
-
#
-
# Example object structure for use with this method:
-
# class Post < ActiveRecord::Base
-
# belongs_to :author
-
# end
-
# class Author < ActiveRecord::Base
-
# has_many :posts
-
# def name_with_initial
-
# "#{first_name.first}. #{last_name}"
-
# end
-
# end
-
#
-
# Sample usage (selecting the associated Author for an instance of Post, <tt>@post</tt>):
-
# collection_select(:post, :author_id, Author.all, :id, :name_with_initial, prompt: true)
-
#
-
# If <tt>@post.author_id</tt> is already <tt>1</tt>, this would return:
-
# <select name="post[author_id]">
-
# <option value="">Please select</option>
-
# <option value="1" selected="selected">D. Heinemeier Hansson</option>
-
# <option value="2">D. Thomas</option>
-
# <option value="3">M. Clark</option>
-
# </select>
-
1
def collection_select(object, method, collection, value_method, text_method, options = {}, html_options = {})
-
Tags::CollectionSelect.new(object, method, self, collection, value_method, text_method, options, html_options).render
-
end
-
-
# Returns <tt><select></tt>, <tt><optgroup></tt> and <tt><option></tt> tags for the collection of existing return values of
-
# +method+ for +object+'s class. The value returned from calling +method+ on the instance +object+ will
-
# be selected. If calling +method+ returns +nil+, no selection is made without including <tt>:prompt</tt>
-
# or <tt>:include_blank</tt> in the +options+ hash.
-
#
-
# Parameters:
-
# * +object+ - The instance of the class to be used for the select tag
-
# * +method+ - The attribute of +object+ corresponding to the select tag
-
# * +collection+ - An array of objects representing the <tt><optgroup></tt> tags.
-
# * +group_method+ - The name of a method which, when called on a member of +collection+, returns an
-
# array of child objects representing the <tt><option></tt> tags.
-
# * +group_label_method+ - The name of a method which, when called on a member of +collection+, returns a
-
# string to be used as the +label+ attribute for its <tt><optgroup></tt> tag.
-
# * +option_key_method+ - The name of a method which, when called on a child object of a member of
-
# +collection+, returns a value to be used as the +value+ attribute for its <tt><option></tt> tag.
-
# * +option_value_method+ - The name of a method which, when called on a child object of a member of
-
# +collection+, returns a value to be used as the contents of its <tt><option></tt> tag.
-
#
-
# Example object structure for use with this method:
-
# class Continent < ActiveRecord::Base
-
# has_many :countries
-
# # attribs: id, name
-
# end
-
# class Country < ActiveRecord::Base
-
# belongs_to :continent
-
# # attribs: id, name, continent_id
-
# end
-
# class City < ActiveRecord::Base
-
# belongs_to :country
-
# # attribs: id, name, country_id
-
# end
-
#
-
# Sample usage:
-
# grouped_collection_select(:city, :country_id, @continents, :countries, :name, :id, :name)
-
#
-
# Possible output:
-
# <select name="city[country_id]">
-
# <optgroup label="Africa">
-
# <option value="1">South Africa</option>
-
# <option value="3">Somalia</option>
-
# </optgroup>
-
# <optgroup label="Europe">
-
# <option value="7" selected="selected">Denmark</option>
-
# <option value="2">Ireland</option>
-
# </optgroup>
-
# </select>
-
#
-
1
def grouped_collection_select(object, method, collection, group_method, group_label_method, option_key_method, option_value_method, options = {}, html_options = {})
-
Tags::GroupedCollectionSelect.new(object, method, self, collection, group_method, group_label_method, option_key_method, option_value_method, options, html_options).render
-
end
-
-
# Return select and option tags for the given object and method, using
-
# #time_zone_options_for_select to generate the list of option tags.
-
#
-
# In addition to the <tt>:include_blank</tt> option documented above,
-
# this method also supports a <tt>:model</tt> option, which defaults
-
# to ActiveSupport::TimeZone. This may be used by users to specify a
-
# different time zone model object. (See +time_zone_options_for_select+
-
# for more information.)
-
#
-
# You can also supply an array of ActiveSupport::TimeZone objects
-
# as +priority_zones+, so that they will be listed above the rest of the
-
# (long) list. (You can use ActiveSupport::TimeZone.us_zones as a convenience
-
# for obtaining a list of the US time zones, or a Regexp to select the zones
-
# of your choice)
-
#
-
# Finally, this method supports a <tt>:default</tt> option, which selects
-
# a default ActiveSupport::TimeZone if the object's time zone is +nil+.
-
#
-
# time_zone_select( "user", "time_zone", nil, include_blank: true)
-
#
-
# time_zone_select( "user", "time_zone", nil, default: "Pacific Time (US & Canada)" )
-
#
-
# time_zone_select( "user", 'time_zone', ActiveSupport::TimeZone.us_zones, default: "Pacific Time (US & Canada)")
-
#
-
# time_zone_select( "user", 'time_zone', [ ActiveSupport::TimeZone['Alaska'], ActiveSupport::TimeZone['Hawaii'] ])
-
#
-
# time_zone_select( "user", 'time_zone', /Australia/)
-
#
-
# time_zone_select( "user", "time_zone", ActiveSupport::TimeZone.all.sort, model: ActiveSupport::TimeZone)
-
1
def time_zone_select(object, method, priority_zones = nil, options = {}, html_options = {})
-
Tags::TimeZoneSelect.new(object, method, self, priority_zones, options, html_options).render
-
end
-
-
# Accepts a container (hash, array, enumerable, your type) and returns a string of option tags. Given a container
-
# where the elements respond to first and last (such as a two-element array), the "lasts" serve as option values and
-
# the "firsts" as option text. Hashes are turned into this form automatically, so the keys become "firsts" and values
-
# become lasts. If +selected+ is specified, the matching "last" or element will get the selected option-tag. +selected+
-
# may also be an array of values to be selected when using a multiple select.
-
#
-
# Examples (call, result):
-
# options_for_select([["Dollar", "$"], ["Kroner", "DKK"]])
-
# # <option value="$">Dollar</option>
-
# # <option value="DKK">Kroner</option>
-
#
-
# options_for_select([ "VISA", "MasterCard" ], "MasterCard")
-
# # <option>VISA</option>
-
# # <option selected="selected">MasterCard</option>
-
#
-
# options_for_select({ "Basic" => "$20", "Plus" => "$40" }, "$40")
-
# # <option value="$20">Basic</option>
-
# # <option value="$40" selected="selected">Plus</option>
-
#
-
# options_for_select([ "VISA", "MasterCard", "Discover" ], ["VISA", "Discover"])
-
# # <option selected="selected">VISA</option>
-
# # <option>MasterCard</option>
-
# # <option selected="selected">Discover</option>
-
#
-
# You can optionally provide html attributes as the last element of the array.
-
#
-
# Examples:
-
# options_for_select([ "Denmark", ["USA", {class: 'bold'}], "Sweden" ], ["USA", "Sweden"])
-
# # <option value="Denmark">Denmark</option>
-
# # <option value="USA" class="bold" selected="selected">USA</option>
-
# # <option value="Sweden" selected="selected">Sweden</option>
-
#
-
# options_for_select([["Dollar", "$", {class: "bold"}], ["Kroner", "DKK", {onclick: "alert('HI');"}]])
-
# # <option value="$" class="bold">Dollar</option>
-
# # <option value="DKK" onclick="alert('HI');">Kroner</option>
-
#
-
# If you wish to specify disabled option tags, set +selected+ to be a hash, with <tt>:disabled</tt> being either a value
-
# or array of values to be disabled. In this case, you can use <tt>:selected</tt> to specify selected option tags.
-
#
-
# Examples:
-
# options_for_select(["Free", "Basic", "Advanced", "Super Platinum"], disabled: "Super Platinum")
-
# # <option value="Free">Free</option>
-
# # <option value="Basic">Basic</option>
-
# # <option value="Advanced">Advanced</option>
-
# # <option value="Super Platinum" disabled="disabled">Super Platinum</option>
-
#
-
# options_for_select(["Free", "Basic", "Advanced", "Super Platinum"], disabled: ["Advanced", "Super Platinum"])
-
# # <option value="Free">Free</option>
-
# # <option value="Basic">Basic</option>
-
# # <option value="Advanced" disabled="disabled">Advanced</option>
-
# # <option value="Super Platinum" disabled="disabled">Super Platinum</option>
-
#
-
# options_for_select(["Free", "Basic", "Advanced", "Super Platinum"], selected: "Free", disabled: "Super Platinum")
-
# # <option value="Free" selected="selected">Free</option>
-
# # <option value="Basic">Basic</option>
-
# # <option value="Advanced">Advanced</option>
-
# # <option value="Super Platinum" disabled="disabled">Super Platinum</option>
-
#
-
# NOTE: Only the option tags are returned, you have to wrap this call in a regular HTML select tag.
-
1
def options_for_select(container, selected = nil)
-
return container if String === container
-
-
selected, disabled = extract_selected_and_disabled(selected).map do |r|
-
Array(r).map { |item| item.to_s }
-
end
-
-
container.map do |element|
-
html_attributes = option_html_attributes(element)
-
text, value = option_text_and_value(element).map { |item| item.to_s }
-
-
html_attributes[:selected] = 'selected' if option_value_selected?(value, selected)
-
html_attributes[:disabled] = 'disabled' if disabled && option_value_selected?(value, disabled)
-
html_attributes[:value] = value
-
-
content_tag_string(:option, text, html_attributes)
-
end.join("\n").html_safe
-
end
-
-
# Returns a string of option tags that have been compiled by iterating over the +collection+ and assigning
-
# the result of a call to the +value_method+ as the option value and the +text_method+ as the option text.
-
# Example:
-
# options_from_collection_for_select(@people, 'id', 'name')
-
# This will output the same HTML as if you did this:
-
# <option value="#{person.id}">#{person.name}</option>
-
#
-
# This is more often than not used inside a #select_tag like this example:
-
# select_tag 'person', options_from_collection_for_select(@people, 'id', 'name')
-
#
-
# If +selected+ is specified as a value or array of values, the element(s) returning a match on +value_method+
-
# will be selected option tag(s).
-
#
-
# If +selected+ is specified as a Proc, those members of the collection that return true for the anonymous
-
# function are the selected values.
-
#
-
# +selected+ can also be a hash, specifying both <tt>:selected</tt> and/or <tt>:disabled</tt> values as required.
-
#
-
# Be sure to specify the same class as the +value_method+ when specifying selected or disabled options.
-
# Failure to do this will produce undesired results. Example:
-
# options_from_collection_for_select(@people, 'id', 'name', '1')
-
# Will not select a person with the id of 1 because 1 (an Integer) is not the same as '1' (a string)
-
# options_from_collection_for_select(@people, 'id', 'name', 1)
-
# should produce the desired results.
-
1
def options_from_collection_for_select(collection, value_method, text_method, selected = nil)
-
options = collection.map do |element|
-
[value_for_collection(element, text_method), value_for_collection(element, value_method)]
-
end
-
selected, disabled = extract_selected_and_disabled(selected)
-
select_deselect = {
-
:selected => extract_values_from_collection(collection, value_method, selected),
-
:disabled => extract_values_from_collection(collection, value_method, disabled)
-
}
-
-
options_for_select(options, select_deselect)
-
end
-
-
# Returns a string of <tt><option></tt> tags, like <tt>options_from_collection_for_select</tt>, but
-
# groups them by <tt><optgroup></tt> tags based on the object relationships of the arguments.
-
#
-
# Parameters:
-
# * +collection+ - An array of objects representing the <tt><optgroup></tt> tags.
-
# * +group_method+ - The name of a method which, when called on a member of +collection+, returns an
-
# array of child objects representing the <tt><option></tt> tags.
-
# * group_label_method+ - The name of a method which, when called on a member of +collection+, returns a
-
# string to be used as the +label+ attribute for its <tt><optgroup></tt> tag.
-
# * +option_key_method+ - The name of a method which, when called on a child object of a member of
-
# +collection+, returns a value to be used as the +value+ attribute for its <tt><option></tt> tag.
-
# * +option_value_method+ - The name of a method which, when called on a child object of a member of
-
# +collection+, returns a value to be used as the contents of its <tt><option></tt> tag.
-
# * +selected_key+ - A value equal to the +value+ attribute for one of the <tt><option></tt> tags,
-
# which will have the +selected+ attribute set. Corresponds to the return value of one of the calls
-
# to +option_key_method+. If +nil+, no selection is made. Can also be a hash if disabled values are
-
# to be specified.
-
#
-
# Example object structure for use with this method:
-
# class Continent < ActiveRecord::Base
-
# has_many :countries
-
# # attribs: id, name
-
# end
-
# class Country < ActiveRecord::Base
-
# belongs_to :continent
-
# # attribs: id, name, continent_id
-
# end
-
#
-
# Sample usage:
-
# option_groups_from_collection_for_select(@continents, :countries, :name, :id, :name, 3)
-
#
-
# Possible output:
-
# <optgroup label="Africa">
-
# <option value="1">Egypt</option>
-
# <option value="4">Rwanda</option>
-
# ...
-
# </optgroup>
-
# <optgroup label="Asia">
-
# <option value="3" selected="selected">China</option>
-
# <option value="12">India</option>
-
# <option value="5">Japan</option>
-
# ...
-
# </optgroup>
-
#
-
# <b>Note:</b> Only the <tt><optgroup></tt> and <tt><option></tt> tags are returned, so you still have to
-
# wrap the output in an appropriate <tt><select></tt> tag.
-
1
def option_groups_from_collection_for_select(collection, group_method, group_label_method, option_key_method, option_value_method, selected_key = nil)
-
collection.map do |group|
-
option_tags = options_from_collection_for_select(
-
group.send(group_method), option_key_method, option_value_method, selected_key)
-
-
content_tag(:optgroup, option_tags, :label => group.send(group_label_method))
-
end.join.html_safe
-
end
-
-
# Returns a string of <tt><option></tt> tags, like <tt>options_for_select</tt>, but
-
# wraps them with <tt><optgroup></tt> tags.
-
#
-
# Parameters:
-
# * +grouped_options+ - Accepts a nested array or hash of strings. The first value serves as the
-
# <tt><optgroup></tt> label while the second value must be an array of options. The second value can be a
-
# nested array of text-value pairs. See <tt>options_for_select</tt> for more info.
-
# Ex. ["North America",[["United States","US"],["Canada","CA"]]]
-
# * +selected_key+ - A value equal to the +value+ attribute for one of the <tt><option></tt> tags,
-
# which will have the +selected+ attribute set. Note: It is possible for this value to match multiple options
-
# as you might have the same option in multiple groups. Each will then get <tt>selected="selected"</tt>.
-
#
-
# Options:
-
# * <tt>:prompt</tt> - set to true or a prompt string. When the select element doesn't have a value yet, this
-
# prepends an option with a generic prompt - "Please select" - or the given prompt string.
-
# * <tt>:divider</tt> - the divider for the options groups.
-
#
-
# Sample usage (Array):
-
# grouped_options = [
-
# ['North America',
-
# [['United States','US'],'Canada']],
-
# ['Europe',
-
# ['Denmark','Germany','France']]
-
# ]
-
# grouped_options_for_select(grouped_options)
-
#
-
# Sample usage (Hash):
-
# grouped_options = {
-
# 'North America' => [['United States','US'], 'Canada'],
-
# 'Europe' => ['Denmark','Germany','France']
-
# }
-
# grouped_options_for_select(grouped_options)
-
#
-
# Possible output:
-
# <optgroup label="Europe">
-
# <option value="Denmark">Denmark</option>
-
# <option value="Germany">Germany</option>
-
# <option value="France">France</option>
-
# </optgroup>
-
# <optgroup label="North America">
-
# <option value="US">United States</option>
-
# <option value="Canada">Canada</option>
-
# </optgroup>
-
#
-
# Sample usage (divider):
-
# grouped_options = [
-
# [['United States','US'], 'Canada'],
-
# ['Denmark','Germany','France']
-
# ]
-
# grouped_options_for_select(grouped_options, nil, divider: '---------')
-
#
-
# Possible output:
-
# <optgroup label="---------">
-
# <option value="US">United States</option>
-
# <option value="Canada">Canada</option>
-
# </optgroup>
-
# <optgroup label="---------">
-
# <option value="Denmark">Denmark</option>
-
# <option value="Germany">Germany</option>
-
# <option value="France">France</option>
-
# </optgroup>
-
#
-
# <b>Note:</b> Only the <tt><optgroup></tt> and <tt><option></tt> tags are returned, so you still have to
-
# wrap the output in an appropriate <tt><select></tt> tag.
-
1
def grouped_options_for_select(grouped_options, selected_key = nil, options = {})
-
if options.is_a?(Hash)
-
prompt = options[:prompt]
-
divider = options[:divider]
-
else
-
prompt = options
-
options = {}
-
message = "Passing the prompt to grouped_options_for_select as an argument is deprecated. " \
-
"Please use an options hash like `{ prompt: #{prompt.inspect} }`."
-
ActiveSupport::Deprecation.warn message
-
end
-
-
body = "".html_safe
-
-
if prompt
-
body.safe_concat content_tag(:option, prompt_text(prompt), :value => "")
-
end
-
-
grouped_options = grouped_options.sort if grouped_options.is_a?(Hash)
-
-
grouped_options.each do |container|
-
if divider
-
label = divider
-
else
-
label, container = container
-
end
-
body.safe_concat content_tag(:optgroup, options_for_select(container, selected_key), :label => label)
-
end
-
-
body
-
end
-
-
# Returns a string of option tags for pretty much any time zone in the
-
# world. Supply a ActiveSupport::TimeZone name as +selected+ to have it
-
# marked as the selected option tag. You can also supply an array of
-
# ActiveSupport::TimeZone objects as +priority_zones+, so that they will
-
# be listed above the rest of the (long) list. (You can use
-
# ActiveSupport::TimeZone.us_zones as a convenience for obtaining a list
-
# of the US time zones, or a Regexp to select the zones of your choice)
-
#
-
# The +selected+ parameter must be either +nil+, or a string that names
-
# a ActiveSupport::TimeZone.
-
#
-
# By default, +model+ is the ActiveSupport::TimeZone constant (which can
-
# be obtained in Active Record as a value object). The only requirement
-
# is that the +model+ parameter be an object that responds to +all+, and
-
# returns an array of objects that represent time zones.
-
#
-
# NOTE: Only the option tags are returned, you have to wrap this call in
-
# a regular HTML select tag.
-
1
def time_zone_options_for_select(selected = nil, priority_zones = nil, model = ::ActiveSupport::TimeZone)
-
zone_options = "".html_safe
-
-
zones = model.all
-
convert_zones = lambda { |list| list.map { |z| [ z.to_s, z.name ] } }
-
-
if priority_zones
-
if priority_zones.is_a?(Regexp)
-
priority_zones = zones.select { |z| z =~ priority_zones }
-
end
-
-
zone_options.safe_concat options_for_select(convert_zones[priority_zones], selected)
-
zone_options.safe_concat content_tag(:option, '-------------', :value => '', :disabled => 'disabled')
-
zone_options.safe_concat "\n"
-
-
zones.reject! { |z| priority_zones.include?(z) }
-
end
-
-
zone_options.safe_concat options_for_select(convert_zones[zones], selected)
-
end
-
-
# Returns radio button tags for the collection of existing return values
-
# of +method+ for +object+'s class. The value returned from calling
-
# +method+ on the instance +object+ will be selected. If calling +method+
-
# returns +nil+, no selection is made.
-
#
-
# The <tt>:value_method</tt> and <tt>:text_method</tt> parameters are
-
# methods to be called on each member of +collection+. The return values
-
# are used as the +value+ attribute and contents of each radio button tag,
-
# respectively. They can also be any object that responds to +call+, such
-
# as a +proc+, that will be called for each member of the +collection+ to
-
# retrieve the value/text.
-
#
-
# Example object structure for use with this method:
-
# class Post < ActiveRecord::Base
-
# belongs_to :author
-
# end
-
# class Author < ActiveRecord::Base
-
# has_many :posts
-
# def name_with_initial
-
# "#{first_name.first}. #{last_name}"
-
# end
-
# end
-
#
-
# Sample usage (selecting the associated Author for an instance of Post, <tt>@post</tt>):
-
# collection_radio_buttons(:post, :author_id, Author.all, :id, :name_with_initial)
-
#
-
# If <tt>@post.author_id</tt> is already <tt>1</tt>, this would return:
-
# <input id="post_author_id_1" name="post[author_id]" type="radio" value="1" checked="checked" />
-
# <label for="post_author_id_1">D. Heinemeier Hansson</label>
-
# <input id="post_author_id_2" name="post[author_id]" type="radio" value="2" />
-
# <label for="post_author_id_2">D. Thomas</label>
-
# <input id="post_author_id_3" name="post[author_id]" type="radio" value="3" />
-
# <label for="post_author_id_3">M. Clark</label>
-
#
-
# It is also possible to customize the way the elements will be shown by
-
# giving a block to the method:
-
# collection_radio_buttons(:post, :author_id, Author.all, :id, :name_with_initial) do |b|
-
# b.label { b.radio_button }
-
# end
-
#
-
# The argument passed to the block is a special kind of builder for this
-
# collection, which has the ability to generate the label and radio button
-
# for the current item in the collection, with proper text and value.
-
# Using it, you can change the label and radio button display order or
-
# even use the label as wrapper, as in the example above.
-
#
-
# The builder methods <tt>label</tt> and <tt>radio_button</tt> also accept
-
# extra html options:
-
# collection_radio_buttons(:post, :author_id, Author.all, :id, :name_with_initial) do |b|
-
# b.label(class: "radio_button") { b.radio_button(class: "radio_button") }
-
# end
-
#
-
# There are also three special methods available: <tt>object</tt>, <tt>text</tt> and
-
# <tt>value</tt>, which are the current item being rendered, its text and value methods,
-
# respectively. You can use them like this:
-
# collection_radio_buttons(:post, :author_id, Author.all, :id, :name_with_initial) do |b|
-
# b.label(:"data-value" => b.value) { b.radio_button + b.text }
-
# end
-
1
def collection_radio_buttons(object, method, collection, value_method, text_method, options = {}, html_options = {}, &block)
-
Tags::CollectionRadioButtons.new(object, method, self, collection, value_method, text_method, options, html_options).render(&block)
-
end
-
-
# Returns check box tags for the collection of existing return values of
-
# +method+ for +object+'s class. The value returned from calling +method+
-
# on the instance +object+ will be selected. If calling +method+ returns
-
# +nil+, no selection is made.
-
#
-
# The <tt>:value_method</tt> and <tt>:text_method</tt> parameters are
-
# methods to be called on each member of +collection+. The return values
-
# are used as the +value+ attribute and contents of each check box tag,
-
# respectively. They can also be any object that responds to +call+, such
-
# as a +proc+, that will be called for each member of the +collection+ to
-
# retrieve the value/text.
-
#
-
# Example object structure for use with this method:
-
# class Post < ActiveRecord::Base
-
# has_and_belongs_to_many :author
-
# end
-
# class Author < ActiveRecord::Base
-
# has_and_belongs_to_many :posts
-
# def name_with_initial
-
# "#{first_name.first}. #{last_name}"
-
# end
-
# end
-
#
-
# Sample usage (selecting the associated Author for an instance of Post, <tt>@post</tt>):
-
# collection_check_boxes(:post, :author_ids, Author.all, :id, :name_with_initial)
-
#
-
# If <tt>@post.author_ids</tt> is already <tt>[1]</tt>, this would return:
-
# <input id="post_author_ids_1" name="post[author_ids][]" type="checkbox" value="1" checked="checked" />
-
# <label for="post_author_ids_1">D. Heinemeier Hansson</label>
-
# <input id="post_author_ids_2" name="post[author_ids][]" type="checkbox" value="2" />
-
# <label for="post_author_ids_2">D. Thomas</label>
-
# <input id="post_author_ids_3" name="post[author_ids][]" type="checkbox" value="3" />
-
# <label for="post_author_ids_3">M. Clark</label>
-
# <input name="post[author_ids][]" type="hidden" value="" />
-
#
-
# It is also possible to customize the way the elements will be shown by
-
# giving a block to the method:
-
# collection_check_boxes(:post, :author_ids, Author.all, :id, :name_with_initial) do |b|
-
# b.label { b.check_box }
-
# end
-
#
-
# The argument passed to the block is a special kind of builder for this
-
# collection, which has the ability to generate the label and check box
-
# for the current item in the collection, with proper text and value.
-
# Using it, you can change the label and check box display order or even
-
# use the label as wrapper, as in the example above.
-
#
-
# The builder methods <tt>label</tt> and <tt>check_box</tt> also accept
-
# extra html options:
-
# collection_check_boxes(:post, :author_ids, Author.all, :id, :name_with_initial) do |b|
-
# b.label(class: "check_box") { b.check_box(class: "check_box") }
-
# end
-
#
-
# There are also three special methods available: <tt>object</tt>, <tt>text</tt> and
-
# <tt>value</tt>, which are the current item being rendered, its text and value methods,
-
# respectively. You can use them like this:
-
# collection_check_boxes(:post, :author_ids, Author.all, :id, :name_with_initial) do |b|
-
# b.label(:"data-value" => b.value) { b.check_box + b.text }
-
# end
-
1
def collection_check_boxes(object, method, collection, value_method, text_method, options = {}, html_options = {}, &block)
-
Tags::CollectionCheckBoxes.new(object, method, self, collection, value_method, text_method, options, html_options).render(&block)
-
end
-
-
1
private
-
1
def option_html_attributes(element)
-
if Array === element
-
element.select { |e| Hash === e }.reduce({}, :merge!)
-
else
-
{}
-
end
-
end
-
-
1
def option_text_and_value(option)
-
# Options are [text, value] pairs or strings used for both.
-
if !option.is_a?(String) && option.respond_to?(:first) && option.respond_to?(:last)
-
option = option.reject { |e| Hash === e } if Array === option
-
[option.first, option.last]
-
else
-
[option, option]
-
end
-
end
-
-
1
def option_value_selected?(value, selected)
-
Array(selected).include? value
-
end
-
-
1
def extract_selected_and_disabled(selected)
-
if selected.is_a?(Proc)
-
[selected, nil]
-
else
-
selected = Array.wrap(selected)
-
options = selected.extract_options!.symbolize_keys
-
selected_items = options.fetch(:selected, selected)
-
[selected_items, options[:disabled]]
-
end
-
end
-
-
1
def extract_values_from_collection(collection, value_method, selected)
-
if selected.is_a?(Proc)
-
collection.map do |element|
-
element.send(value_method) if selected.call(element)
-
end.compact
-
else
-
selected
-
end
-
end
-
-
1
def value_for_collection(item, value)
-
value.respond_to?(:call) ? value.call(item) : item.send(value)
-
end
-
-
1
def prompt_text(prompt)
-
prompt = prompt.kind_of?(String) ? prompt : I18n.translate('helpers.select.prompt', :default => 'Please select')
-
end
-
end
-
-
1
class FormBuilder
-
1
def select(method, choices, options = {}, html_options = {})
-
@template.select(@object_name, method, choices, objectify_options(options), @default_options.merge(html_options))
-
end
-
-
1
def collection_select(method, collection, value_method, text_method, options = {}, html_options = {})
-
@template.collection_select(@object_name, method, collection, value_method, text_method, objectify_options(options), @default_options.merge(html_options))
-
end
-
-
1
def grouped_collection_select(method, collection, group_method, group_label_method, option_key_method, option_value_method, options = {}, html_options = {})
-
@template.grouped_collection_select(@object_name, method, collection, group_method, group_label_method, option_key_method, option_value_method, objectify_options(options), @default_options.merge(html_options))
-
end
-
-
1
def time_zone_select(method, priority_zones = nil, options = {}, html_options = {})
-
@template.time_zone_select(@object_name, method, priority_zones, objectify_options(options), @default_options.merge(html_options))
-
end
-
-
1
def collection_check_boxes(method, collection, value_method, text_method, options = {}, html_options = {})
-
@template.collection_check_boxes(@object_name, method, collection, value_method, text_method, objectify_options(options), @default_options.merge(html_options))
-
end
-
-
1
def collection_radio_buttons(method, collection, value_method, text_method, options = {}, html_options = {})
-
@template.collection_radio_buttons(@object_name, method, collection, value_method, text_method, objectify_options(options), @default_options.merge(html_options))
-
end
-
end
-
end
-
end
-
1
require 'cgi'
-
1
require 'action_view/helpers/tag_helper'
-
1
require 'active_support/core_ext/string/output_safety'
-
1
require 'active_support/core_ext/module/attribute_accessors'
-
-
1
module ActionView
-
# = Action View Form Tag Helpers
-
1
module Helpers
-
# Provides a number of methods for creating form tags that don't rely on an Active Record object assigned to the template like
-
# FormHelper does. Instead, you provide the names and values manually.
-
#
-
# NOTE: The HTML options <tt>disabled</tt>, <tt>readonly</tt>, and <tt>multiple</tt> can all be treated as booleans. So specifying
-
# <tt>disabled: true</tt> will give <tt>disabled="disabled"</tt>.
-
1
module FormTagHelper
-
1
extend ActiveSupport::Concern
-
-
1
include UrlHelper
-
1
include TextHelper
-
-
1
mattr_accessor :embed_authenticity_token_in_remote_forms
-
1
self.embed_authenticity_token_in_remote_forms = false
-
-
# Starts a form tag that points the action to an url configured with <tt>url_for_options</tt> just like
-
# ActionController::Base#url_for. The method for the form defaults to POST.
-
#
-
# ==== Options
-
# * <tt>:multipart</tt> - If set to true, the enctype is set to "multipart/form-data".
-
# * <tt>:method</tt> - The method to use when submitting the form, usually either "get" or "post".
-
# If "put", "delete", or another verb is used, a hidden input with name <tt>_method</tt>
-
# is added to simulate the verb over post.
-
# * <tt>:authenticity_token</tt> - Authenticity token to use in the form. Use only if you need to
-
# pass custom authenticity token string, or to not add authenticity_token field at all
-
# (by passing <tt>false</tt>). Remote forms may omit the embedded authenticity token
-
# by setting <tt>config.action_view.embed_authenticity_token_in_remote_forms = false</tt>.
-
# This is helpful when you're fragment-caching the form. Remote forms get the
-
# authenticity from the <tt>meta</tt> tag, so embedding is unnecessary unless you
-
# support browsers without JavaScript.
-
# * A list of parameters to feed to the URL the form will be posted to.
-
# * <tt>:remote</tt> - If set to true, will allow the Unobtrusive JavaScript drivers to control the
-
# submit behavior. By default this behavior is an ajax submit.
-
#
-
# ==== Examples
-
# form_tag('/posts')
-
# # => <form action="/posts" method="post">
-
#
-
# form_tag('/posts/1', method: :put)
-
# # => <form action="/posts/1" method="post"> ... <input name="_method" type="hidden" value="put" /> ...
-
#
-
# form_tag('/upload', multipart: true)
-
# # => <form action="/upload" method="post" enctype="multipart/form-data">
-
#
-
# <%= form_tag('/posts') do -%>
-
# <div><%= submit_tag 'Save' %></div>
-
# <% end -%>
-
# # => <form action="/posts" method="post"><div><input type="submit" name="submit" value="Save" /></div></form>
-
#
-
# <%= form_tag('/posts', remote: true) %>
-
# # => <form action="/posts" method="post" data-remote="true">
-
#
-
# form_tag('http://far.away.com/form', authenticity_token: false)
-
# # form without authenticity token
-
#
-
# form_tag('http://far.away.com/form', authenticity_token: "cf50faa3fe97702ca1ae")
-
# # form with custom authenticity token
-
#
-
1
def form_tag(url_for_options = {}, options = {}, &block)
-
html_options = html_options_for_form(url_for_options, options)
-
if block_given?
-
form_tag_in_block(html_options, &block)
-
else
-
form_tag_html(html_options)
-
end
-
end
-
-
# Creates a dropdown selection box, or if the <tt>:multiple</tt> option is set to true, a multiple
-
# choice selection box.
-
#
-
# Helpers::FormOptions can be used to create common select boxes such as countries, time zones, or
-
# associated records. <tt>option_tags</tt> is a string containing the option tags for the select box.
-
#
-
# ==== Options
-
# * <tt>:multiple</tt> - If set to true the selection will allow multiple choices.
-
# * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
-
# * <tt>:include_blank</tt> - If set to true, an empty option will be create
-
# * <tt>:prompt</tt> - Create a prompt option with blank value and the text asking user to select something
-
# * Any other key creates standard HTML attributes for the tag.
-
#
-
# ==== Examples
-
# select_tag "people", options_from_collection_for_select(@people, "id", "name")
-
# # <select id="people" name="people"><option value="1">David</option></select>
-
#
-
# select_tag "people", "<option>David</option>".html_safe
-
# # => <select id="people" name="people"><option>David</option></select>
-
#
-
# select_tag "count", "<option>1</option><option>2</option><option>3</option><option>4</option>".html_safe
-
# # => <select id="count" name="count"><option>1</option><option>2</option>
-
# # <option>3</option><option>4</option></select>
-
#
-
# select_tag "colors", "<option>Red</option><option>Green</option><option>Blue</option>".html_safe, multiple: true
-
# # => <select id="colors" multiple="multiple" name="colors[]"><option>Red</option>
-
# # <option>Green</option><option>Blue</option></select>
-
#
-
# select_tag "locations", "<option>Home</option><option selected='selected'>Work</option><option>Out</option>".html_safe
-
# # => <select id="locations" name="locations"><option>Home</option><option selected='selected'>Work</option>
-
# # <option>Out</option></select>
-
#
-
# select_tag "access", "<option>Read</option><option>Write</option>".html_safe, multiple: true, class: 'form_input'
-
# # => <select class="form_input" id="access" multiple="multiple" name="access[]"><option>Read</option>
-
# # <option>Write</option></select>
-
#
-
# select_tag "people", options_from_collection_for_select(@people, "id", "name"), include_blank: true
-
# # => <select id="people" name="people"><option value=""></option><option value="1">David</option></select>
-
#
-
# select_tag "people", options_from_collection_for_select(@people, "id", "name"), prompt: "Select something"
-
# # => <select id="people" name="people"><option value="">Select something</option><option value="1">David</option></select>
-
#
-
# select_tag "destination", "<option>NYC</option><option>Paris</option><option>Rome</option>".html_safe, disabled: true
-
# # => <select disabled="disabled" id="destination" name="destination"><option>NYC</option>
-
# # <option>Paris</option><option>Rome</option></select>
-
#
-
# select_tag "credit_card", options_for_select([ "VISA", "MasterCard" ], "MasterCard")
-
# # => <select id="credit_card" name="credit_card"><option>VISA</option>
-
# # <option selected="selected">MasterCard</option></select>
-
1
def select_tag(name, option_tags = nil, options = {})
-
option_tags ||= ""
-
html_name = (options[:multiple] == true && !name.to_s.ends_with?("[]")) ? "#{name}[]" : name
-
-
if options.delete(:include_blank)
-
option_tags = content_tag(:option, '', :value => '').safe_concat(option_tags)
-
end
-
-
if prompt = options.delete(:prompt)
-
option_tags = content_tag(:option, prompt, :value => '').safe_concat(option_tags)
-
end
-
-
content_tag :select, option_tags, { "name" => html_name, "id" => sanitize_to_id(name) }.update(options.stringify_keys)
-
end
-
-
# Creates a standard text field; use these text fields to input smaller chunks of text like a username
-
# or a search query.
-
#
-
# ==== Options
-
# * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
-
# * <tt>:size</tt> - The number of visible characters that will fit in the input.
-
# * <tt>:maxlength</tt> - The maximum number of characters that the browser will allow the user to enter.
-
# * <tt>:placeholder</tt> - The text contained in the field by default which is removed when the field receives focus.
-
# * Any other key creates standard HTML attributes for the tag.
-
#
-
# ==== Examples
-
# text_field_tag 'name'
-
# # => <input id="name" name="name" type="text" />
-
#
-
# text_field_tag 'query', 'Enter your search query here'
-
# # => <input id="query" name="query" type="text" value="Enter your search query here" />
-
#
-
# text_field_tag 'search', nil, placeholder: 'Enter search term...'
-
# # => <input id="search" name="search" placeholder="Enter search term..." type="text" />
-
#
-
# text_field_tag 'request', nil, class: 'special_input'
-
# # => <input class="special_input" id="request" name="request" type="text" />
-
#
-
# text_field_tag 'address', '', size: 75
-
# # => <input id="address" name="address" size="75" type="text" value="" />
-
#
-
# text_field_tag 'zip', nil, maxlength: 5
-
# # => <input id="zip" maxlength="5" name="zip" type="text" />
-
#
-
# text_field_tag 'payment_amount', '$0.00', disabled: true
-
# # => <input disabled="disabled" id="payment_amount" name="payment_amount" type="text" value="$0.00" />
-
#
-
# text_field_tag 'ip', '0.0.0.0', maxlength: 15, size: 20, class: "ip-input"
-
# # => <input class="ip-input" id="ip" maxlength="15" name="ip" size="20" type="text" value="0.0.0.0" />
-
1
def text_field_tag(name, value = nil, options = {})
-
tag :input, { "type" => "text", "name" => name, "id" => sanitize_to_id(name), "value" => value }.update(options.stringify_keys)
-
end
-
-
# Creates a label element. Accepts a block.
-
#
-
# ==== Options
-
# * Creates standard HTML attributes for the tag.
-
#
-
# ==== Examples
-
# label_tag 'name'
-
# # => <label for="name">Name</label>
-
#
-
# label_tag 'name', 'Your name'
-
# # => <label for="name">Your Name</label>
-
#
-
# label_tag 'name', nil, class: 'small_label'
-
# # => <label for="name" class="small_label">Name</label>
-
1
def label_tag(name = nil, content_or_options = nil, options = nil, &block)
-
if block_given? && content_or_options.is_a?(Hash)
-
options = content_or_options = content_or_options.stringify_keys
-
else
-
options ||= {}
-
options = options.stringify_keys
-
end
-
options["for"] = sanitize_to_id(name) unless name.blank? || options.has_key?("for")
-
content_tag :label, content_or_options || name.to_s.humanize, options, &block
-
end
-
-
# Creates a hidden form input field used to transmit data that would be lost due to HTTP's statelessness or
-
# data that should be hidden from the user.
-
#
-
# ==== Options
-
# * Creates standard HTML attributes for the tag.
-
#
-
# ==== Examples
-
# hidden_field_tag 'tags_list'
-
# # => <input id="tags_list" name="tags_list" type="hidden" />
-
#
-
# hidden_field_tag 'token', 'VUBJKB23UIVI1UU1VOBVI@'
-
# # => <input id="token" name="token" type="hidden" value="VUBJKB23UIVI1UU1VOBVI@" />
-
#
-
# hidden_field_tag 'collected_input', '', onchange: "alert('Input collected!')"
-
# # => <input id="collected_input" name="collected_input" onchange="alert('Input collected!')"
-
# # type="hidden" value="" />
-
1
def hidden_field_tag(name, value = nil, options = {})
-
text_field_tag(name, value, options.stringify_keys.update("type" => "hidden"))
-
end
-
-
# Creates a file upload field. If you are using file uploads then you will also need
-
# to set the multipart option for the form tag:
-
#
-
# <%= form_tag '/upload', multipart: true do %>
-
# <label for="file">File to Upload</label> <%= file_field_tag "file" %>
-
# <%= submit_tag %>
-
# <% end %>
-
#
-
# The specified URL will then be passed a File object containing the selected file, or if the field
-
# was left blank, a StringIO object.
-
#
-
# ==== Options
-
# * Creates standard HTML attributes for the tag.
-
# * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
-
#
-
# ==== Examples
-
# file_field_tag 'attachment'
-
# # => <input id="attachment" name="attachment" type="file" />
-
#
-
# file_field_tag 'avatar', class: 'profile_input'
-
# # => <input class="profile_input" id="avatar" name="avatar" type="file" />
-
#
-
# file_field_tag 'picture', disabled: true
-
# # => <input disabled="disabled" id="picture" name="picture" type="file" />
-
#
-
# file_field_tag 'resume', value: '~/resume.doc'
-
# # => <input id="resume" name="resume" type="file" value="~/resume.doc" />
-
#
-
# file_field_tag 'user_pic', accept: 'image/png,image/gif,image/jpeg'
-
# # => <input accept="image/png,image/gif,image/jpeg" id="user_pic" name="user_pic" type="file" />
-
#
-
# file_field_tag 'file', accept: 'text/html', class: 'upload', value: 'index.html'
-
# # => <input accept="text/html" class="upload" id="file" name="file" type="file" value="index.html" />
-
1
def file_field_tag(name, options = {})
-
text_field_tag(name, nil, options.update("type" => "file"))
-
end
-
-
# Creates a password field, a masked text field that will hide the users input behind a mask character.
-
#
-
# ==== Options
-
# * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
-
# * <tt>:size</tt> - The number of visible characters that will fit in the input.
-
# * <tt>:maxlength</tt> - The maximum number of characters that the browser will allow the user to enter.
-
# * Any other key creates standard HTML attributes for the tag.
-
#
-
# ==== Examples
-
# password_field_tag 'pass'
-
# # => <input id="pass" name="pass" type="password" />
-
#
-
# password_field_tag 'secret', 'Your secret here'
-
# # => <input id="secret" name="secret" type="password" value="Your secret here" />
-
#
-
# password_field_tag 'masked', nil, class: 'masked_input_field'
-
# # => <input class="masked_input_field" id="masked" name="masked" type="password" />
-
#
-
# password_field_tag 'token', '', size: 15
-
# # => <input id="token" name="token" size="15" type="password" value="" />
-
#
-
# password_field_tag 'key', nil, maxlength: 16
-
# # => <input id="key" maxlength="16" name="key" type="password" />
-
#
-
# password_field_tag 'confirm_pass', nil, disabled: true
-
# # => <input disabled="disabled" id="confirm_pass" name="confirm_pass" type="password" />
-
#
-
# password_field_tag 'pin', '1234', maxlength: 4, size: 6, class: "pin_input"
-
# # => <input class="pin_input" id="pin" maxlength="4" name="pin" size="6" type="password" value="1234" />
-
1
def password_field_tag(name = "password", value = nil, options = {})
-
text_field_tag(name, value, options.update("type" => "password"))
-
end
-
-
# Creates a text input area; use a textarea for longer text inputs such as blog posts or descriptions.
-
#
-
# ==== Options
-
# * <tt>:size</tt> - A string specifying the dimensions (columns by rows) of the textarea (e.g., "25x10").
-
# * <tt>:rows</tt> - Specify the number of rows in the textarea
-
# * <tt>:cols</tt> - Specify the number of columns in the textarea
-
# * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
-
# * <tt>:escape</tt> - By default, the contents of the text input are HTML escaped.
-
# If you need unescaped contents, set this to false.
-
# * Any other key creates standard HTML attributes for the tag.
-
#
-
# ==== Examples
-
# text_area_tag 'post'
-
# # => <textarea id="post" name="post"></textarea>
-
#
-
# text_area_tag 'bio', @user.bio
-
# # => <textarea id="bio" name="bio">This is my biography.</textarea>
-
#
-
# text_area_tag 'body', nil, rows: 10, cols: 25
-
# # => <textarea cols="25" id="body" name="body" rows="10"></textarea>
-
#
-
# text_area_tag 'body', nil, size: "25x10"
-
# # => <textarea name="body" id="body" cols="25" rows="10"></textarea>
-
#
-
# text_area_tag 'description', "Description goes here.", disabled: true
-
# # => <textarea disabled="disabled" id="description" name="description">Description goes here.</textarea>
-
#
-
# text_area_tag 'comment', nil, class: 'comment_input'
-
# # => <textarea class="comment_input" id="comment" name="comment"></textarea>
-
1
def text_area_tag(name, content = nil, options = {})
-
options = options.stringify_keys
-
-
if size = options.delete("size")
-
options["cols"], options["rows"] = size.split("x") if size.respond_to?(:split)
-
end
-
-
escape = options.delete("escape") { true }
-
content = ERB::Util.html_escape(content) if escape
-
-
content_tag :textarea, content.to_s.html_safe, { "name" => name, "id" => sanitize_to_id(name) }.update(options)
-
end
-
-
# Creates a check box form input tag.
-
#
-
# ==== Options
-
# * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
-
# * Any other key creates standard HTML options for the tag.
-
#
-
# ==== Examples
-
# check_box_tag 'accept'
-
# # => <input id="accept" name="accept" type="checkbox" value="1" />
-
#
-
# check_box_tag 'rock', 'rock music'
-
# # => <input id="rock" name="rock" type="checkbox" value="rock music" />
-
#
-
# check_box_tag 'receive_email', 'yes', true
-
# # => <input checked="checked" id="receive_email" name="receive_email" type="checkbox" value="yes" />
-
#
-
# check_box_tag 'tos', 'yes', false, class: 'accept_tos'
-
# # => <input class="accept_tos" id="tos" name="tos" type="checkbox" value="yes" />
-
#
-
# check_box_tag 'eula', 'accepted', false, disabled: true
-
# # => <input disabled="disabled" id="eula" name="eula" type="checkbox" value="accepted" />
-
1
def check_box_tag(name, value = "1", checked = false, options = {})
-
html_options = { "type" => "checkbox", "name" => name, "id" => sanitize_to_id(name), "value" => value }.update(options.stringify_keys)
-
html_options["checked"] = "checked" if checked
-
tag :input, html_options
-
end
-
-
# Creates a radio button; use groups of radio buttons named the same to allow users to
-
# select from a group of options.
-
#
-
# ==== Options
-
# * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
-
# * Any other key creates standard HTML options for the tag.
-
#
-
# ==== Examples
-
# radio_button_tag 'gender', 'male'
-
# # => <input id="gender_male" name="gender" type="radio" value="male" />
-
#
-
# radio_button_tag 'receive_updates', 'no', true
-
# # => <input checked="checked" id="receive_updates_no" name="receive_updates" type="radio" value="no" />
-
#
-
# radio_button_tag 'time_slot', "3:00 p.m.", false, disabled: true
-
# # => <input disabled="disabled" id="time_slot_300_pm" name="time_slot" type="radio" value="3:00 p.m." />
-
#
-
# radio_button_tag 'color', "green", true, class: "color_input"
-
# # => <input checked="checked" class="color_input" id="color_green" name="color" type="radio" value="green" />
-
1
def radio_button_tag(name, value, checked = false, options = {})
-
html_options = { "type" => "radio", "name" => name, "id" => "#{sanitize_to_id(name)}_#{sanitize_to_id(value)}", "value" => value }.update(options.stringify_keys)
-
html_options["checked"] = "checked" if checked
-
tag :input, html_options
-
end
-
-
# Creates a submit button with the text <tt>value</tt> as the caption.
-
#
-
# ==== Options
-
# * <tt>:data</tt> - This option can be used to add custom data attributes.
-
# * <tt>:disabled</tt> - If true, the user will not be able to use this input.
-
# * Any other key creates standard HTML options for the tag.
-
#
-
# ==== Data attributes
-
#
-
# * <tt>confirm: 'question?'</tt> - If present the unobtrusive JavaScript
-
# drivers will provide a prompt with the question specified. If the user accepts,
-
# the form is processed normally, otherwise no action is taken.
-
# * <tt>:disable_with</tt> - Value of this parameter will be used as the value for a
-
# disabled version of the submit button when the form is submitted. This feature is
-
# provided by the unobtrusive JavaScript driver.
-
#
-
# ==== Examples
-
# submit_tag
-
# # => <input name="commit" type="submit" value="Save changes" />
-
#
-
# submit_tag "Edit this article"
-
# # => <input name="commit" type="submit" value="Edit this article" />
-
#
-
# submit_tag "Save edits", disabled: true
-
# # => <input disabled="disabled" name="commit" type="submit" value="Save edits" />
-
#
-
# submit_tag "Complete sale", data: { disable_with: "Please wait..." }
-
# # => <input name="commit" data-disable-with="Please wait..." type="submit" value="Complete sale" />
-
#
-
# submit_tag nil, class: "form_submit"
-
# # => <input class="form_submit" name="commit" type="submit" />
-
#
-
# submit_tag "Edit", class: "edit_button"
-
# # => <input class="edit_button" name="commit" type="submit" value="Edit" />
-
#
-
# submit_tag "Save", data: { confirm: "Are you sure?" }
-
# # => <input name='commit' type='submit' value='Save' data-confirm="Are you sure?" />
-
#
-
1
def submit_tag(value = "Save changes", options = {})
-
options = options.stringify_keys
-
-
if disable_with = options.delete("disable_with")
-
message = ":disable_with option is deprecated and will be removed from Rails 4.1. " \
-
"Use 'data: { disable_with: \'Text\' }' instead."
-
ActiveSupport::Deprecation.warn message
-
-
options["data-disable-with"] = disable_with
-
end
-
-
if confirm = options.delete("confirm")
-
message = ":confirm option is deprecated and will be removed from Rails 4.1. " \
-
"Use 'data: { confirm: \'Text\' }' instead'."
-
ActiveSupport::Deprecation.warn message
-
-
options["data-confirm"] = confirm
-
end
-
-
tag :input, { "type" => "submit", "name" => "commit", "value" => value }.update(options)
-
end
-
-
# Creates a button element that defines a <tt>submit</tt> button,
-
# <tt>reset</tt>button or a generic button which can be used in
-
# JavaScript, for example. You can use the button tag as a regular
-
# submit tag but it isn't supported in legacy browsers. However,
-
# the button tag allows richer labels such as images and emphasis,
-
# so this helper will also accept a block.
-
#
-
# ==== Options
-
# * <tt>:data</tt> - This option can be used to add custom data attributes.
-
# * <tt>:disabled</tt> - If true, the user will not be able to
-
# use this input.
-
# * Any other key creates standard HTML options for the tag.
-
#
-
# ==== Data attributes
-
#
-
# * <tt>confirm: 'question?'</tt> - If present, the
-
# unobtrusive JavaScript drivers will provide a prompt with
-
# the question specified. If the user accepts, the form is
-
# processed normally, otherwise no action is taken.
-
# * <tt>:disable_with</tt> - Value of this parameter will be
-
# used as the value for a disabled version of the submit
-
# button when the form is submitted. This feature is provided
-
# by the unobtrusive JavaScript driver.
-
#
-
# ==== Examples
-
# button_tag
-
# # => <button name="button" type="submit">Button</button>
-
#
-
# button_tag(type: 'button') do
-
# content_tag(:strong, 'Ask me!')
-
# end
-
# # => <button name="button" type="button">
-
# # <strong>Ask me!</strong>
-
# # </button>
-
#
-
# button_tag "Checkout", data: { disable_with => "Please wait..." }
-
# # => <button data-disable-with="Please wait..." name="button" type="submit">Checkout</button>
-
#
-
1
def button_tag(content_or_options = nil, options = nil, &block)
-
options = content_or_options if block_given? && content_or_options.is_a?(Hash)
-
options ||= {}
-
options = options.stringify_keys
-
-
if disable_with = options.delete("disable_with")
-
message = ":disable_with option is deprecated and will be removed from Rails 4.1. " \
-
"Use 'data: { disable_with: \'Text\' }' instead."
-
ActiveSupport::Deprecation.warn message
-
-
options["data-disable-with"] = disable_with
-
end
-
-
if confirm = options.delete("confirm")
-
message = ":confirm option is deprecated and will be removed from Rails 4.1. " \
-
"Use 'data: { confirm: \'Text\' }' instead'."
-
ActiveSupport::Deprecation.warn message
-
-
options["data-confirm"] = confirm
-
end
-
-
options.reverse_merge! 'name' => 'button', 'type' => 'submit'
-
-
content_tag :button, content_or_options || 'Button', options, &block
-
end
-
-
# Displays an image which when clicked will submit the form.
-
#
-
# <tt>source</tt> is passed to AssetTagHelper#path_to_image
-
#
-
# ==== Options
-
# * <tt>:data</tt> - This option can be used to add custom data attributes.
-
# * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
-
# * Any other key creates standard HTML options for the tag.
-
#
-
# ==== Data attributes
-
#
-
# * <tt>confirm: 'question?'</tt> - This will add a JavaScript confirm
-
# prompt with the question specified. If the user accepts, the form is
-
# processed normally, otherwise no action is taken.
-
#
-
# ==== Examples
-
# image_submit_tag("login.png")
-
# # => <input src="/images/login.png" type="image" />
-
#
-
# image_submit_tag("purchase.png", disabled: true)
-
# # => <input disabled="disabled" src="/images/purchase.png" type="image" />
-
#
-
# image_submit_tag("search.png", class: 'search_button')
-
# # => <input class="search_button" src="/images/search.png" type="image" />
-
#
-
# image_submit_tag("agree.png", disabled: true, class: "agree_disagree_button")
-
# # => <input class="agree_disagree_button" disabled="disabled" src="/images/agree.png" type="image" />
-
#
-
# image_submit_tag("save.png", data: { confirm: "Are you sure?" })
-
# # => <input src="/images/save.png" data-confirm="Are you sure?" type="image" />
-
1
def image_submit_tag(source, options = {})
-
options = options.stringify_keys
-
-
if confirm = options.delete("confirm")
-
message = ":confirm option is deprecated and will be removed from Rails 4.1. " \
-
"Use 'data: { confirm: \'Text\' }' instead'."
-
ActiveSupport::Deprecation.warn message
-
-
options["data-confirm"] = confirm
-
end
-
-
tag :input, { "type" => "image", "src" => path_to_image(source) }.update(options)
-
end
-
-
# Creates a field set for grouping HTML form elements.
-
#
-
# <tt>legend</tt> will become the fieldset's title (optional as per W3C).
-
# <tt>options</tt> accept the same values as tag.
-
#
-
# ==== Examples
-
# <%= field_set_tag do %>
-
# <p><%= text_field_tag 'name' %></p>
-
# <% end %>
-
# # => <fieldset><p><input id="name" name="name" type="text" /></p></fieldset>
-
#
-
# <%= field_set_tag 'Your details' do %>
-
# <p><%= text_field_tag 'name' %></p>
-
# <% end %>
-
# # => <fieldset><legend>Your details</legend><p><input id="name" name="name" type="text" /></p></fieldset>
-
#
-
# <%= field_set_tag nil, class: 'format' do %>
-
# <p><%= text_field_tag 'name' %></p>
-
# <% end %>
-
# # => <fieldset class="format"><p><input id="name" name="name" type="text" /></p></fieldset>
-
1
def field_set_tag(legend = nil, options = nil, &block)
-
output = tag(:fieldset, options, true)
-
output.safe_concat(content_tag(:legend, legend)) unless legend.blank?
-
output.concat(capture(&block)) if block_given?
-
output.safe_concat("</fieldset>")
-
end
-
-
# Creates a text field of type "color".
-
#
-
# ==== Options
-
# * Accepts the same options as text_field_tag.
-
1
def color_field_tag(name, value = nil, options = {})
-
text_field_tag(name, value, options.stringify_keys.update("type" => "color"))
-
end
-
-
# Creates a text field of type "search".
-
#
-
# ==== Options
-
# * Accepts the same options as text_field_tag.
-
1
def search_field_tag(name, value = nil, options = {})
-
text_field_tag(name, value, options.stringify_keys.update("type" => "search"))
-
end
-
-
# Creates a text field of type "tel".
-
#
-
# ==== Options
-
# * Accepts the same options as text_field_tag.
-
1
def telephone_field_tag(name, value = nil, options = {})
-
text_field_tag(name, value, options.stringify_keys.update("type" => "tel"))
-
end
-
1
alias phone_field_tag telephone_field_tag
-
-
# Creates a text field of type "date".
-
#
-
# ==== Options
-
# * Accepts the same options as text_field_tag.
-
1
def date_field_tag(name, value = nil, options = {})
-
text_field_tag(name, value, options.stringify_keys.update("type" => "date"))
-
end
-
-
# Creates a text field of type "time".
-
#
-
# === Options
-
# * <tt>:min</tt> - The minimum acceptable value.
-
# * <tt>:max</tt> - The maximum acceptable value.
-
# * <tt>:step</tt> - The acceptable value granularity.
-
# * Otherwise accepts the same options as text_field_tag.
-
1
def time_field_tag(name, value = nil, options = {})
-
text_field_tag(name, value, options.stringify_keys.update("type" => "time"))
-
end
-
-
# Creates a text field of type "datetime".
-
#
-
# === Options
-
# * <tt>:min</tt> - The minimum acceptable value.
-
# * <tt>:max</tt> - The maximum acceptable value.
-
# * <tt>:step</tt> - The acceptable value granularity.
-
# * Otherwise accepts the same options as text_field_tag.
-
1
def datetime_field_tag(name, value = nil, options = {})
-
text_field_tag(name, value, options.stringify_keys.update("type" => "datetime"))
-
end
-
-
# Creates a text field of type "datetime-local".
-
#
-
# === Options
-
# * <tt>:min</tt> - The minimum acceptable value.
-
# * <tt>:max</tt> - The maximum acceptable value.
-
# * <tt>:step</tt> - The acceptable value granularity.
-
# * Otherwise accepts the same options as text_field_tag.
-
1
def datetime_local_field_tag(name, value = nil, options = {})
-
text_field_tag(name, value, options.stringify_keys.update("type" => "datetime-local"))
-
end
-
-
# Creates a text field of type "month".
-
#
-
# === Options
-
# * <tt>:min</tt> - The minimum acceptable value.
-
# * <tt>:max</tt> - The maximum acceptable value.
-
# * <tt>:step</tt> - The acceptable value granularity.
-
# * Otherwise accepts the same options as text_field_tag.
-
1
def month_field_tag(name, value = nil, options = {})
-
text_field_tag(name, value, options.stringify_keys.update("type" => "month"))
-
end
-
-
# Creates a text field of type "week".
-
#
-
# === Options
-
# * <tt>:min</tt> - The minimum acceptable value.
-
# * <tt>:max</tt> - The maximum acceptable value.
-
# * <tt>:step</tt> - The acceptable value granularity.
-
# * Otherwise accepts the same options as text_field_tag.
-
1
def week_field_tag(name, value = nil, options = {})
-
text_field_tag(name, value, options.stringify_keys.update("type" => "week"))
-
end
-
-
# Creates a text field of type "url".
-
#
-
# ==== Options
-
# * Accepts the same options as text_field_tag.
-
1
def url_field_tag(name, value = nil, options = {})
-
text_field_tag(name, value, options.stringify_keys.update("type" => "url"))
-
end
-
-
# Creates a text field of type "email".
-
#
-
# ==== Options
-
# * Accepts the same options as text_field_tag.
-
1
def email_field_tag(name, value = nil, options = {})
-
text_field_tag(name, value, options.stringify_keys.update("type" => "email"))
-
end
-
-
# Creates a number field.
-
#
-
# ==== Options
-
# * <tt>:min</tt> - The minimum acceptable value.
-
# * <tt>:max</tt> - The maximum acceptable value.
-
# * <tt>:in</tt> - A range specifying the <tt>:min</tt> and
-
# <tt>:max</tt> values.
-
# * <tt>:step</tt> - The acceptable value granularity.
-
# * Otherwise accepts the same options as text_field_tag.
-
#
-
# ==== Examples
-
# number_field_tag 'quantity', nil, in: 1...10
-
# # => <input id="quantity" name="quantity" min="1" max="9" type="number" />
-
1
def number_field_tag(name, value = nil, options = {})
-
options = options.stringify_keys
-
options["type"] ||= "number"
-
if range = options.delete("in") || options.delete("within")
-
options.update("min" => range.min, "max" => range.max)
-
end
-
text_field_tag(name, value, options)
-
end
-
-
# Creates a range form element.
-
#
-
# ==== Options
-
# * Accepts the same options as number_field_tag.
-
1
def range_field_tag(name, value = nil, options = {})
-
number_field_tag(name, value, options.stringify_keys.update("type" => "range"))
-
end
-
-
# Creates the hidden UTF8 enforcer tag. Override this method in a helper
-
# to customize the tag.
-
1
def utf8_enforcer_tag
-
tag(:input, :type => "hidden", :name => "utf8", :value => "✓".html_safe)
-
end
-
-
1
private
-
1
def html_options_for_form(url_for_options, options)
-
options.stringify_keys.tap do |html_options|
-
html_options["enctype"] = "multipart/form-data" if html_options.delete("multipart")
-
# The following URL is unescaped, this is just a hash of options, and it is the
-
# responsibility of the caller to escape all the values.
-
html_options["action"] = url_for(url_for_options)
-
html_options["accept-charset"] = "UTF-8"
-
-
html_options["data-remote"] = true if html_options.delete("remote")
-
-
if html_options["data-remote"] &&
-
!embed_authenticity_token_in_remote_forms &&
-
html_options["authenticity_token"].blank?
-
# The authenticity token is taken from the meta tag in this case
-
html_options["authenticity_token"] = false
-
elsif html_options["authenticity_token"] == true
-
# Include the default authenticity_token, which is only generated when its set to nil,
-
# but we needed the true value to override the default of no authenticity_token on data-remote.
-
html_options["authenticity_token"] = nil
-
end
-
end
-
end
-
-
1
def extra_tags_for_form(html_options)
-
authenticity_token = html_options.delete("authenticity_token")
-
method = html_options.delete("method").to_s
-
-
method_tag = case method
-
when /^get$/i # must be case-insensitive, but can't use downcase as might be nil
-
html_options["method"] = "get"
-
''
-
when /^post$/i, "", nil
-
html_options["method"] = "post"
-
token_tag(authenticity_token)
-
else
-
html_options["method"] = "post"
-
method_tag(method) + token_tag(authenticity_token)
-
end
-
-
tags = utf8_enforcer_tag << method_tag
-
content_tag(:div, tags, :style => 'margin:0;padding:0;display:inline')
-
end
-
-
1
def form_tag_html(html_options)
-
extra_tags = extra_tags_for_form(html_options)
-
tag(:form, html_options, true) + extra_tags
-
end
-
-
1
def form_tag_in_block(html_options, &block)
-
content = capture(&block)
-
output = form_tag_html(html_options)
-
output << content
-
output.safe_concat("</form>")
-
end
-
-
# see http://www.w3.org/TR/html4/types.html#type-name
-
1
def sanitize_to_id(name)
-
name.to_s.gsub(']','').gsub(/[^-a-zA-Z0-9:.]/, "_")
-
end
-
end
-
end
-
end
-
1
require 'action_view/helpers/tag_helper'
-
-
1
module ActionView
-
1
module Helpers
-
1
module JavaScriptHelper
-
1
JS_ESCAPE_MAP = {
-
'\\' => '\\\\',
-
'</' => '<\/',
-
"\r\n" => '\n',
-
"\n" => '\n',
-
"\r" => '\n',
-
'"' => '\\"',
-
"'" => "\\'"
-
}
-
-
1
JS_ESCAPE_MAP["\342\200\250".force_encoding('UTF-8').encode!] = '
'
-
1
JS_ESCAPE_MAP["\342\200\251".force_encoding('UTF-8').encode!] = '
'
-
-
# Escapes carriage returns and single and double quotes for JavaScript segments.
-
#
-
# Also available through the alias j(). This is particularly helpful in JavaScript responses, like:
-
#
-
# $('some_element').replaceWith('<%=j render 'some/element_template' %>');
-
1
def escape_javascript(javascript)
-
if javascript
-
result = javascript.gsub(/(\\|<\/|\r\n|\342\200\250|\342\200\251|[\n\r"'])/u) {|match| JS_ESCAPE_MAP[match] }
-
javascript.html_safe? ? result.html_safe : result
-
else
-
''
-
end
-
end
-
-
1
alias_method :j, :escape_javascript
-
-
# Returns a JavaScript tag with the +content+ inside. Example:
-
# javascript_tag "alert('All is good')"
-
#
-
# Returns:
-
# <script>
-
# //<![CDATA[
-
# alert('All is good')
-
# //]]>
-
# </script>
-
#
-
# +html_options+ may be a hash of attributes for the <tt>\<script></tt>
-
# tag. Example:
-
# javascript_tag "alert('All is good')", defer: 'defer'
-
# # => <script defer="defer">alert('All is good')</script>
-
#
-
# Instead of passing the content as an argument, you can also use a block
-
# in which case, you pass your +html_options+ as the first parameter.
-
# <%= javascript_tag defer: 'defer' do -%>
-
# alert('All is good')
-
# <% end -%>
-
1
def javascript_tag(content_or_options_with_block = nil, html_options = {}, &block)
-
content =
-
if block_given?
-
html_options = content_or_options_with_block if content_or_options_with_block.is_a?(Hash)
-
capture(&block)
-
else
-
content_or_options_with_block
-
end
-
-
content_tag(:script, javascript_cdata_section(content), html_options)
-
end
-
-
1
def javascript_cdata_section(content) #:nodoc:
-
"\n//#{cdata_section("\n#{content}\n//")}\n".html_safe
-
end
-
-
# Returns a button whose +onclick+ handler triggers the passed JavaScript.
-
#
-
# The helper receives a name, JavaScript code, and an optional hash of HTML options. The
-
# name is used as button label and the JavaScript code goes into its +onclick+ attribute.
-
# If +html_options+ has an <tt>:onclick</tt>, that one is put before +function+.
-
#
-
# button_to_function "Greeting", "alert('Hello world!')", class: "ok"
-
# # => <input class="ok" onclick="alert('Hello world!');" type="button" value="Greeting" />
-
#
-
1
def button_to_function(name, function=nil, html_options={})
-
message = "button_to_function is deprecated and will be removed from Rails 4.1. We recomend to use Unobtrusive JavaScript instead. " +
-
"See http://guides.rubyonrails.org/working_with_javascript_in_rails.html#unobtrusive-javascript"
-
ActiveSupport::Deprecation.warn message
-
-
onclick = "#{"#{html_options[:onclick]}; " if html_options[:onclick]}#{function};"
-
-
tag(:input, html_options.merge(:type => 'button', :value => name, :onclick => onclick))
-
end
-
-
# Returns a link whose +onclick+ handler triggers the passed JavaScript.
-
#
-
# The helper receives a name, JavaScript code, and an optional hash of HTML options. The
-
# name is used as the link text and the JavaScript code goes into the +onclick+ attribute.
-
# If +html_options+ has an <tt>:onclick</tt>, that one is put before +function+. Once all
-
# the JavaScript is set, the helper appends "; return false;".
-
#
-
# The +href+ attribute of the tag is set to "#" unless +html_options+ has one.
-
#
-
# link_to_function "Greeting", "alert('Hello world!')", class: "nav_link"
-
# # => <a class="nav_link" href="#" onclick="alert('Hello world!'); return false;">Greeting</a>
-
#
-
1
def link_to_function(name, function, html_options={})
-
message = "link_to_function is deprecated and will be removed from Rails 4.1. We recomend to use Unobtrusive JavaScript instead. " +
-
"See http://guides.rubyonrails.org/working_with_javascript_in_rails.html#unobtrusive-javascript"
-
ActiveSupport::Deprecation.warn message
-
-
onclick = "#{"#{html_options[:onclick]}; " if html_options[:onclick]}#{function}; return false;"
-
href = html_options[:href] || '#'
-
-
content_tag(:a, name, html_options.merge(:href => href, :onclick => onclick))
-
end
-
end
-
end
-
end
-
# encoding: utf-8
-
-
1
require 'active_support/core_ext/hash/keys'
-
1
require 'active_support/core_ext/string/output_safety'
-
1
require 'active_support/number_helper'
-
-
1
module ActionView
-
# = Action View Number Helpers
-
1
module Helpers #:nodoc:
-
-
# Provides methods for converting numbers into formatted strings.
-
# Methods are provided for phone numbers, currency, percentage,
-
# precision, positional notation, file size and pretty printing.
-
#
-
# Most methods expect a +number+ argument, and will return it
-
# unchanged if can't be converted into a valid number.
-
1
module NumberHelper
-
-
# Raised when argument +number+ param given to the helpers is invalid and
-
# the option :raise is set to +true+.
-
1
class InvalidNumberError < StandardError
-
1
attr_accessor :number
-
1
def initialize(number)
-
@number = number
-
end
-
end
-
-
# Formats a +number+ into a US phone number (e.g., (555)
-
# 123-9876). You can customize the format in the +options+ hash.
-
#
-
# ==== Options
-
#
-
# * <tt>:area_code</tt> - Adds parentheses around the area code.
-
# * <tt>:delimiter</tt> - Specifies the delimiter to use
-
# (defaults to "-").
-
# * <tt>:extension</tt> - Specifies an extension to add to the
-
# end of the generated number.
-
# * <tt>:country_code</tt> - Sets the country code for the phone
-
# number.
-
# * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when
-
# the argument is invalid.
-
#
-
# ==== Examples
-
#
-
# number_to_phone(5551234) # => 555-1234
-
# number_to_phone("5551234") # => 555-1234
-
# number_to_phone(1235551234) # => 123-555-1234
-
# number_to_phone(1235551234, area_code: true) # => (123) 555-1234
-
# number_to_phone(1235551234, delimiter: " ") # => 123 555 1234
-
# number_to_phone(1235551234, area_code: true, extension: 555) # => (123) 555-1234 x 555
-
# number_to_phone(1235551234, country_code: 1) # => +1-123-555-1234
-
# number_to_phone("123a456") # => 123a456
-
#
-
# number_to_phone("1234a567", raise: true) # => InvalidNumberError
-
#
-
# number_to_phone(1235551234, country_code: 1, extension: 1343, delimiter: ".")
-
# # => +1.123.555.1234 x 1343
-
1
def number_to_phone(number, options = {})
-
return unless number
-
options = options.symbolize_keys
-
-
parse_float(number, true) if options.delete(:raise)
-
ERB::Util.html_escape(ActiveSupport::NumberHelper.number_to_phone(number, options))
-
end
-
-
# Formats a +number+ into a currency string (e.g., $13.65). You
-
# can customize the format in the +options+ hash.
-
#
-
# ==== Options
-
#
-
# * <tt>:locale</tt> - Sets the locale to be used for formatting
-
# (defaults to current locale).
-
# * <tt>:precision</tt> - Sets the level of precision (defaults
-
# to 2).
-
# * <tt>:unit</tt> - Sets the denomination of the currency
-
# (defaults to "$").
-
# * <tt>:separator</tt> - Sets the separator between the units
-
# (defaults to ".").
-
# * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults
-
# to ",").
-
# * <tt>:format</tt> - Sets the format for non-negative numbers
-
# (defaults to "%u%n"). Fields are <tt>%u</tt> for the
-
# currency, and <tt>%n</tt> for the number.
-
# * <tt>:negative_format</tt> - Sets the format for negative
-
# numbers (defaults to prepending an hyphen to the formatted
-
# number given by <tt>:format</tt>). Accepts the same fields
-
# than <tt>:format</tt>, except <tt>%n</tt> is here the
-
# absolute value of the number.
-
# * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when
-
# the argument is invalid.
-
#
-
# ==== Examples
-
#
-
# number_to_currency(1234567890.50) # => $1,234,567,890.50
-
# number_to_currency(1234567890.506) # => $1,234,567,890.51
-
# number_to_currency(1234567890.506, precision: 3) # => $1,234,567,890.506
-
# number_to_currency(1234567890.506, locale: :fr) # => 1 234 567 890,51 €
-
# number_to_currency("123a456") # => $123a456
-
#
-
# number_to_currency("123a456", raise: true) # => InvalidNumberError
-
#
-
# number_to_currency(-1234567890.50, negative_format: "(%u%n)")
-
# # => ($1,234,567,890.50)
-
# number_to_currency(1234567890.50, unit: "£", separator: ",", delimiter: "")
-
# # => £1234567890,50
-
# number_to_currency(1234567890.50, unit: "£", separator: ",", delimiter: "", format: "%n %u")
-
# # => 1234567890,50 £
-
1
def number_to_currency(number, options = {})
-
return unless number
-
options = escape_unsafe_delimiters_and_separators(options.symbolize_keys)
-
-
wrap_with_output_safety_handling(number, options.delete(:raise)) {
-
ActiveSupport::NumberHelper.number_to_currency(number, options)
-
}
-
end
-
-
# Formats a +number+ as a percentage string (e.g., 65%). You can
-
# customize the format in the +options+ hash.
-
#
-
# ==== Options
-
#
-
# * <tt>:locale</tt> - Sets the locale to be used for formatting
-
# (defaults to current locale).
-
# * <tt>:precision</tt> - Sets the precision of the number
-
# (defaults to 3).
-
# * <tt>:significant</tt> - If +true+, precision will be the #
-
# of significant_digits. If +false+, the # of fractional
-
# digits (defaults to +false+).
-
# * <tt>:separator</tt> - Sets the separator between the
-
# fractional and integer digits (defaults to ".").
-
# * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults
-
# to "").
-
# * <tt>:strip_insignificant_zeros</tt> - If +true+ removes
-
# insignificant zeros after the decimal separator (defaults to
-
# +false+).
-
# * <tt>:format</tt> - Specifies the format of the percentage
-
# string The number field is <tt>%n</tt> (defaults to "%n%").
-
# * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when
-
# the argument is invalid.
-
#
-
# ==== Examples
-
#
-
# number_to_percentage(100) # => 100.000%
-
# number_to_percentage("98") # => 98.000%
-
# number_to_percentage(100, precision: 0) # => 100%
-
# number_to_percentage(1000, delimiter: '.', separator: ',') # => 1.000,000%
-
# number_to_percentage(302.24398923423, precision: 5) # => 302.24399%
-
# number_to_percentage(1000, locale: :fr) # => 1 000,000%
-
# number_to_percentage("98a") # => 98a%
-
# number_to_percentage(100, format: "%n %") # => 100 %
-
#
-
# number_to_percentage("98a", raise: true) # => InvalidNumberError
-
1
def number_to_percentage(number, options = {})
-
return unless number
-
options = escape_unsafe_delimiters_and_separators(options.symbolize_keys)
-
-
wrap_with_output_safety_handling(number, options.delete(:raise)) {
-
ActiveSupport::NumberHelper.number_to_percentage(number, options)
-
}
-
end
-
-
# Formats a +number+ with grouped thousands using +delimiter+
-
# (e.g., 12,324). You can customize the format in the +options+
-
# hash.
-
#
-
# ==== Options
-
#
-
# * <tt>:locale</tt> - Sets the locale to be used for formatting
-
# (defaults to current locale).
-
# * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults
-
# to ",").
-
# * <tt>:separator</tt> - Sets the separator between the
-
# fractional and integer digits (defaults to ".").
-
# * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when
-
# the argument is invalid.
-
#
-
# ==== Examples
-
#
-
# number_with_delimiter(12345678) # => 12,345,678
-
# number_with_delimiter("123456") # => 123,456
-
# number_with_delimiter(12345678.05) # => 12,345,678.05
-
# number_with_delimiter(12345678, delimiter: ".") # => 12.345.678
-
# number_with_delimiter(12345678, delimiter: ",") # => 12,345,678
-
# number_with_delimiter(12345678.05, separator: " ") # => 12,345,678 05
-
# number_with_delimiter(12345678.05, locale: :fr) # => 12 345 678,05
-
# number_with_delimiter("112a") # => 112a
-
# number_with_delimiter(98765432.98, delimiter: " ", separator: ",")
-
# # => 98 765 432,98
-
#
-
# number_with_delimiter("112a", raise: true) # => raise InvalidNumberError
-
1
def number_with_delimiter(number, options = {})
-
options = escape_unsafe_delimiters_and_separators(options.symbolize_keys)
-
-
wrap_with_output_safety_handling(number, options.delete(:raise)) {
-
ActiveSupport::NumberHelper.number_to_delimited(number, options)
-
}
-
end
-
-
# Formats a +number+ with the specified level of
-
# <tt>:precision</tt> (e.g., 112.32 has a precision of 2 if
-
# +:significant+ is +false+, and 5 if +:significant+ is +true+).
-
# You can customize the format in the +options+ hash.
-
#
-
# ==== Options
-
#
-
# * <tt>:locale</tt> - Sets the locale to be used for formatting
-
# (defaults to current locale).
-
# * <tt>:precision</tt> - Sets the precision of the number
-
# (defaults to 3).
-
# * <tt>:significant</tt> - If +true+, precision will be the #
-
# of significant_digits. If +false+, the # of fractional
-
# digits (defaults to +false+).
-
# * <tt>:separator</tt> - Sets the separator between the
-
# fractional and integer digits (defaults to ".").
-
# * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults
-
# to "").
-
# * <tt>:strip_insignificant_zeros</tt> - If +true+ removes
-
# insignificant zeros after the decimal separator (defaults to
-
# +false+).
-
# * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when
-
# the argument is invalid.
-
#
-
# ==== Examples
-
#
-
# number_with_precision(111.2345) # => 111.235
-
# number_with_precision(111.2345, precision: 2) # => 111.23
-
# number_with_precision(13, precision: 5) # => 13.00000
-
# number_with_precision(389.32314, precision: 0) # => 389
-
# number_with_precision(111.2345, significant: true) # => 111
-
# number_with_precision(111.2345, precision: 1, significant: true) # => 100
-
# number_with_precision(13, precision: 5, significant: true) # => 13.000
-
# number_with_precision(111.234, locale: :fr) # => 111,234
-
#
-
# number_with_precision(13, precision: 5, significant: true, strip_insignificant_zeros: true)
-
# # => 13
-
#
-
# number_with_precision(389.32314, precision: 4, significant: true) # => 389.3
-
# number_with_precision(1111.2345, precision: 2, separator: ',', delimiter: '.')
-
# # => 1.111,23
-
1
def number_with_precision(number, options = {})
-
options = escape_unsafe_delimiters_and_separators(options.symbolize_keys)
-
-
wrap_with_output_safety_handling(number, options.delete(:raise)) {
-
ActiveSupport::NumberHelper.number_to_rounded(number, options)
-
}
-
end
-
-
-
# Formats the bytes in +number+ into a more understandable
-
# representation (e.g., giving it 1500 yields 1.5 KB). This
-
# method is useful for reporting file sizes to users. You can
-
# customize the format in the +options+ hash.
-
#
-
# See <tt>number_to_human</tt> if you want to pretty-print a
-
# generic number.
-
#
-
# ==== Options
-
#
-
# * <tt>:locale</tt> - Sets the locale to be used for formatting
-
# (defaults to current locale).
-
# * <tt>:precision</tt> - Sets the precision of the number
-
# (defaults to 3).
-
# * <tt>:significant</tt> - If +true+, precision will be the #
-
# of significant_digits. If +false+, the # of fractional
-
# digits (defaults to +true+)
-
# * <tt>:separator</tt> - Sets the separator between the
-
# fractional and integer digits (defaults to ".").
-
# * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults
-
# to "").
-
# * <tt>:strip_insignificant_zeros</tt> - If +true+ removes
-
# insignificant zeros after the decimal separator (defaults to
-
# +true+)
-
# * <tt>:prefix</tt> - If +:si+ formats the number using the SI
-
# prefix (defaults to :binary)
-
# * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when
-
# the argument is invalid.
-
#
-
# ==== Examples
-
#
-
# number_to_human_size(123) # => 123 Bytes
-
# number_to_human_size(1234) # => 1.21 KB
-
# number_to_human_size(12345) # => 12.1 KB
-
# number_to_human_size(1234567) # => 1.18 MB
-
# number_to_human_size(1234567890) # => 1.15 GB
-
# number_to_human_size(1234567890123) # => 1.12 TB
-
# number_to_human_size(1234567, precision: 2) # => 1.2 MB
-
# number_to_human_size(483989, precision: 2) # => 470 KB
-
# number_to_human_size(1234567, precision: 2, separator: ',') # => 1,2 MB
-
#
-
# Non-significant zeros after the fractional separator are
-
# stripped out by default (set
-
# <tt>:strip_insignificant_zeros</tt> to +false+ to change
-
# that):
-
# number_to_human_size(1234567890123, precision: 5) # => "1.1229 TB"
-
# number_to_human_size(524288000, precision: 5) # => "500 MB"
-
1
def number_to_human_size(number, options = {})
-
options = escape_unsafe_delimiters_and_separators(options.symbolize_keys)
-
-
wrap_with_output_safety_handling(number, options.delete(:raise)) {
-
ActiveSupport::NumberHelper.number_to_human_size(number, options)
-
}
-
end
-
-
# Pretty prints (formats and approximates) a number in a way it
-
# is more readable by humans (eg.: 1200000000 becomes "1.2
-
# Billion"). This is useful for numbers that can get very large
-
# (and too hard to read).
-
#
-
# See <tt>number_to_human_size</tt> if you want to print a file
-
# size.
-
#
-
# You can also define you own unit-quantifier names if you want
-
# to use other decimal units (eg.: 1500 becomes "1.5
-
# kilometers", 0.150 becomes "150 milliliters", etc). You may
-
# define a wide range of unit quantifiers, even fractional ones
-
# (centi, deci, mili, etc).
-
#
-
# ==== Options
-
#
-
# * <tt>:locale</tt> - Sets the locale to be used for formatting
-
# (defaults to current locale).
-
# * <tt>:precision</tt> - Sets the precision of the number
-
# (defaults to 3).
-
# * <tt>:significant</tt> - If +true+, precision will be the #
-
# of significant_digits. If +false+, the # of fractional
-
# digits (defaults to +true+)
-
# * <tt>:separator</tt> - Sets the separator between the
-
# fractional and integer digits (defaults to ".").
-
# * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults
-
# to "").
-
# * <tt>:strip_insignificant_zeros</tt> - If +true+ removes
-
# insignificant zeros after the decimal separator (defaults to
-
# +true+)
-
# * <tt>:units</tt> - A Hash of unit quantifier names. Or a
-
# string containing an i18n scope where to find this hash. It
-
# might have the following keys:
-
# * *integers*: <tt>:unit</tt>, <tt>:ten</tt>,
-
# *<tt>:hundred</tt>, <tt>:thousand</tt>, <tt>:million</tt>,
-
# *<tt>:billion</tt>, <tt>:trillion</tt>,
-
# *<tt>:quadrillion</tt>
-
# * *fractionals*: <tt>:deci</tt>, <tt>:centi</tt>,
-
# *<tt>:mili</tt>, <tt>:micro</tt>, <tt>:nano</tt>,
-
# *<tt>:pico</tt>, <tt>:femto</tt>
-
# * <tt>:format</tt> - Sets the format of the output string
-
# (defaults to "%n %u"). The field types are:
-
# * %u - The quantifier (ex.: 'thousand')
-
# * %n - The number
-
# * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when
-
# the argument is invalid.
-
#
-
# ==== Examples
-
#
-
# number_to_human(123) # => "123"
-
# number_to_human(1234) # => "1.23 Thousand"
-
# number_to_human(12345) # => "12.3 Thousand"
-
# number_to_human(1234567) # => "1.23 Million"
-
# number_to_human(1234567890) # => "1.23 Billion"
-
# number_to_human(1234567890123) # => "1.23 Trillion"
-
# number_to_human(1234567890123456) # => "1.23 Quadrillion"
-
# number_to_human(1234567890123456789) # => "1230 Quadrillion"
-
# number_to_human(489939, precision: 2) # => "490 Thousand"
-
# number_to_human(489939, precision: 4) # => "489.9 Thousand"
-
# number_to_human(1234567, precision: 4,
-
# significant: false) # => "1.2346 Million"
-
# number_to_human(1234567, precision: 1,
-
# separator: ',',
-
# significant: false) # => "1,2 Million"
-
#
-
# Non-significant zeros after the decimal separator are stripped
-
# out by default (set <tt>:strip_insignificant_zeros</tt> to
-
# +false+ to change that):
-
# number_to_human(12345012345, significant_digits: 6) # => "12.345 Billion"
-
# number_to_human(500000000, precision: 5) # => "500 Million"
-
#
-
# ==== Custom Unit Quantifiers
-
#
-
# You can also use your own custom unit quantifiers:
-
# number_to_human(500000, units: {unit: "ml", thousand: "lt"}) # => "500 lt"
-
#
-
# If in your I18n locale you have:
-
# distance:
-
# centi:
-
# one: "centimeter"
-
# other: "centimeters"
-
# unit:
-
# one: "meter"
-
# other: "meters"
-
# thousand:
-
# one: "kilometer"
-
# other: "kilometers"
-
# billion: "gazillion-distance"
-
#
-
# Then you could do:
-
#
-
# number_to_human(543934, units: :distance) # => "544 kilometers"
-
# number_to_human(54393498, units: :distance) # => "54400 kilometers"
-
# number_to_human(54393498000, units: :distance) # => "54.4 gazillion-distance"
-
# number_to_human(343, units: :distance, precision: 1) # => "300 meters"
-
# number_to_human(1, units: :distance) # => "1 meter"
-
# number_to_human(0.34, units: :distance) # => "34 centimeters"
-
#
-
1
def number_to_human(number, options = {})
-
options = escape_unsafe_delimiters_and_separators(options.symbolize_keys)
-
-
wrap_with_output_safety_handling(number, options.delete(:raise)) {
-
ActiveSupport::NumberHelper.number_to_human(number, options)
-
}
-
end
-
-
1
private
-
-
1
def escape_unsafe_delimiters_and_separators(options)
-
options[:separator] = ERB::Util.html_escape(options[:separator]) if options[:separator] && !options[:separator].html_safe?
-
options[:delimiter] = ERB::Util.html_escape(options[:delimiter]) if options[:delimiter] && !options[:delimiter].html_safe?
-
options
-
end
-
-
1
def wrap_with_output_safety_handling(number, raise_on_invalid, &block)
-
valid_float = valid_float?(number)
-
raise InvalidNumberError, number if raise_on_invalid && !valid_float
-
-
formatted_number = yield
-
-
if valid_float || number.html_safe?
-
formatted_number.html_safe
-
else
-
formatted_number
-
end
-
end
-
-
1
def valid_float?(number)
-
!parse_float(number, false).nil?
-
end
-
-
1
def parse_float(number, raise_error)
-
Float(number)
-
rescue ArgumentError, TypeError
-
raise InvalidNumberError, number if raise_error
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/string/output_safety'
-
-
1
module ActionView #:nodoc:
-
# = Action View Raw Output Helper
-
1
module Helpers #:nodoc:
-
1
module OutputSafetyHelper
-
# This method outputs without escaping a string. Since escaping tags is
-
# now default, this can be used when you don't want Rails to automatically
-
# escape tags. This is not recommended if the data is coming from the user's
-
# input.
-
#
-
# For example:
-
#
-
# <%=raw @user.name %>
-
1
def raw(stringish)
-
stringish.to_s.html_safe
-
end
-
-
# This method returns a html safe string similar to what <tt>Array#join</tt>
-
# would return. All items in the array, including the supplied separator, are
-
# html escaped unless they are html safe, and the returned string is marked
-
# as html safe.
-
#
-
# safe_join(["<p>foo</p>".html_safe, "<p>bar</p>"], "<br />")
-
# # => "<p>foo</p><br /><p>bar</p>"
-
#
-
# safe_join(["<p>foo</p>".html_safe, "<p>bar</p>".html_safe], "<br />".html_safe)
-
# # => "<p>foo</p><br /><p>bar</p>"
-
#
-
1
def safe_join(array, sep=$,)
-
sep = ERB::Util.html_escape(sep)
-
-
array.map { |i| ERB::Util.html_escape(i) }.join(sep).html_safe
-
end
-
end
-
end
-
end
-
1
module ActionView
-
# = Action View Record Tag Helpers
-
1
module Helpers
-
1
module RecordTagHelper
-
1
include ActionView::RecordIdentifier
-
-
# Produces a wrapper DIV element with id and class parameters that
-
# relate to the specified Active Record object. Usage example:
-
#
-
# <%= div_for(@person, class: "foo") do %>
-
# <%= @person.name %>
-
# <% end %>
-
#
-
# produces:
-
#
-
# <div id="person_123" class="person foo"> Joe Bloggs </div>
-
#
-
# You can also pass an array of Active Record objects, which will then
-
# get iterated over and yield each record as an argument for the block.
-
# For example:
-
#
-
# <%= div_for(@people, class: "foo") do |person| %>
-
# <%= person.name %>
-
# <% end %>
-
#
-
# produces:
-
#
-
# <div id="person_123" class="person foo"> Joe Bloggs </div>
-
# <div id="person_124" class="person foo"> Jane Bloggs </div>
-
#
-
1
def div_for(record, *args, &block)
-
content_tag_for(:div, record, *args, &block)
-
end
-
-
# content_tag_for creates an HTML element with id and class parameters
-
# that relate to the specified Active Record object. For example:
-
#
-
# <%= content_tag_for(:tr, @person) do %>
-
# <td><%= @person.first_name %></td>
-
# <td><%= @person.last_name %></td>
-
# <% end %>
-
#
-
# would produce the following HTML (assuming @person is an instance of
-
# a Person object, with an id value of 123):
-
#
-
# <tr id="person_123" class="person">....</tr>
-
#
-
# If you require the HTML id attribute to have a prefix, you can specify it:
-
#
-
# <%= content_tag_for(:tr, @person, :foo) do %> ...
-
#
-
# produces:
-
#
-
# <tr id="foo_person_123" class="person">...
-
#
-
# You can also pass an array of objects which this method will loop through
-
# and yield the current object to the supplied block, reducing the need for
-
# having to iterate through the object (using <tt>each</tt>) beforehand.
-
# For example (assuming @people is an array of Person objects):
-
#
-
# <%= content_tag_for(:tr, @people) do |person| %>
-
# <td><%= person.first_name %></td>
-
# <td><%= person.last_name %></td>
-
# <% end %>
-
#
-
# produces:
-
#
-
# <tr id="person_123" class="person">...</tr>
-
# <tr id="person_124" class="person">...</tr>
-
#
-
# content_tag_for also accepts a hash of options, which will be converted to
-
# additional HTML attributes. If you specify a <tt>:class</tt> value, it will be combined
-
# with the default class name for your object. For example:
-
#
-
# <%= content_tag_for(:li, @person, class: "bar") %>...
-
#
-
# produces:
-
#
-
# <li id="person_123" class="person bar">...
-
#
-
1
def content_tag_for(tag_name, single_or_multiple_records, prefix = nil, options = nil, &block)
-
options, prefix = prefix, nil if prefix.is_a?(Hash)
-
-
Array(single_or_multiple_records).map do |single_record|
-
content_tag_for_single_record(tag_name, single_record, prefix, options, &block)
-
end.join("\n").html_safe
-
end
-
-
1
private
-
-
# Called by <tt>content_tag_for</tt> internally to render a content tag
-
# for each record.
-
1
def content_tag_for_single_record(tag_name, record, prefix, options, &block)
-
options = options ? options.dup : {}
-
options[:class] = "#{dom_class(record, prefix)} #{options[:class]}".rstrip
-
options[:id] = dom_id(record, prefix)
-
-
content_tag(tag_name, capture(record, &block), options)
-
end
-
end
-
end
-
end
-
1
module ActionView
-
1
module Helpers
-
# = Action View Rendering
-
#
-
# Implements methods that allow rendering from a view context.
-
# In order to use this module, all you need is to implement
-
# view_renderer that returns an ActionView::Renderer object.
-
1
module RenderingHelper
-
# Returns the result of a render that's dictated by the options hash. The primary options are:
-
#
-
# * <tt>:partial</tt> - See <tt>ActionView::PartialRenderer</tt>.
-
# * <tt>:file</tt> - Renders an explicit template file (this used to be the old default), add :locals to pass in those.
-
# * <tt>:inline</tt> - Renders an inline template similar to how it's done in the controller.
-
# * <tt>:text</tt> - Renders the text passed in out.
-
#
-
# If no options hash is passed or :update specified, the default is to render a partial and use the second parameter
-
# as the locals hash.
-
1
def render(options = {}, locals = {}, &block)
-
case options
-
when Hash
-
if block_given?
-
view_renderer.render_partial(self, options.merge(:partial => options[:layout]), &block)
-
else
-
view_renderer.render(self, options)
-
end
-
else
-
view_renderer.render_partial(self, :partial => options, :locals => locals)
-
end
-
end
-
-
# Overwrites _layout_for in the context object so it supports the case a block is
-
# passed to a partial. Returns the contents that are yielded to a layout, given a
-
# name or a block.
-
#
-
# You can think of a layout as a method that is called with a block. If the user calls
-
# <tt>yield :some_name</tt>, the block, by default, returns <tt>content_for(:some_name)</tt>.
-
# If the user calls simply +yield+, the default block returns <tt>content_for(:layout)</tt>.
-
#
-
# The user can override this default by passing a block to the layout:
-
#
-
# # The template
-
# <%= render layout: "my_layout" do %>
-
# Content
-
# <% end %>
-
#
-
# # The layout
-
# <html>
-
# <%= yield %>
-
# </html>
-
#
-
# In this case, instead of the default block, which would return <tt>content_for(:layout)</tt>,
-
# this method returns the block that was passed in to <tt>render :layout</tt>, and the response
-
# would be
-
#
-
# <html>
-
# Content
-
# </html>
-
#
-
# Finally, the block can take block arguments, which can be passed in by +yield+:
-
#
-
# # The template
-
# <%= render layout: "my_layout" do |customer| %>
-
# Hello <%= customer.name %>
-
# <% end %>
-
#
-
# # The layout
-
# <html>
-
# <%= yield Struct.new(:name).new("David") %>
-
# </html>
-
#
-
# In this case, the layout would receive the block passed into <tt>render :layout</tt>,
-
# and the struct specified would be passed into the block as an argument. The result
-
# would be
-
#
-
# <html>
-
# Hello David
-
# </html>
-
#
-
1
def _layout_for(*args, &block)
-
9
name = args.first
-
-
9
if block && !name.is_a?(Symbol)
-
capture(*args, &block)
-
else
-
9
super
-
end
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/object/try'
-
1
require 'action_view/vendor/html-scanner'
-
-
1
module ActionView
-
# = Action View Sanitize Helpers
-
1
module Helpers #:nodoc:
-
# The SanitizeHelper module provides a set of methods for scrubbing text of undesired HTML elements.
-
# These helper methods extend Action View making them callable within your template files.
-
1
module SanitizeHelper
-
1
extend ActiveSupport::Concern
-
# This +sanitize+ helper will html encode all tags and strip all attributes that
-
# aren't specifically allowed.
-
#
-
# It also strips href/src tags with invalid protocols, like javascript: especially.
-
# It does its best to counter any tricks that hackers may use, like throwing in
-
# unicode/ascii/hex values to get past the javascript: filters. Check out
-
# the extensive test suite.
-
#
-
# <%= sanitize @article.body %>
-
#
-
# You can add or remove tags/attributes if you want to customize it a bit.
-
# See ActionView::Base for full docs on the available options. You can add
-
# tags/attributes for single uses of +sanitize+ by passing either the
-
# <tt>:attributes</tt> or <tt>:tags</tt> options:
-
#
-
# Normal Use
-
#
-
# <%= sanitize @article.body %>
-
#
-
# Custom Use (only the mentioned tags and attributes are allowed, nothing else)
-
#
-
# <%= sanitize @article.body, tags: %w(table tr td), attributes: %w(id class style) %>
-
#
-
# Add table tags to the default allowed tags
-
#
-
# class Application < Rails::Application
-
# config.action_view.sanitized_allowed_tags = 'table', 'tr', 'td'
-
# end
-
#
-
# Remove tags to the default allowed tags
-
#
-
# class Application < Rails::Application
-
# config.after_initialize do
-
# ActionView::Base.sanitized_allowed_tags.delete 'div'
-
# end
-
# end
-
#
-
# Change allowed default attributes
-
#
-
# class Application < Rails::Application
-
# config.action_view.sanitized_allowed_attributes = 'id', 'class', 'style'
-
# end
-
#
-
# Please note that sanitizing user-provided text does not guarantee that the
-
# resulting markup is valid (conforming to a document type) or even well-formed.
-
# The output may still contain e.g. unescaped '<', '>', '&' characters and
-
# confuse browsers.
-
#
-
1
def sanitize(html, options = {})
-
self.class.white_list_sanitizer.sanitize(html, options).try(:html_safe)
-
end
-
-
# Sanitizes a block of CSS code. Used by +sanitize+ when it comes across a style attribute.
-
1
def sanitize_css(style)
-
self.class.white_list_sanitizer.sanitize_css(style)
-
end
-
-
# Strips all HTML tags from the +html+, including comments. This uses the
-
# html-scanner tokenizer and so its HTML parsing ability is limited by
-
# that of html-scanner.
-
#
-
# strip_tags("Strip <i>these</i> tags!")
-
# # => Strip these tags!
-
#
-
# strip_tags("<b>Bold</b> no more! <a href='more.html'>See more here</a>...")
-
# # => Bold no more! See more here...
-
#
-
# strip_tags("<div id='top-bar'>Welcome to my website!</div>")
-
# # => Welcome to my website!
-
1
def strip_tags(html)
-
self.class.full_sanitizer.sanitize(html)
-
end
-
-
# Strips all link tags from +text+ leaving just the link text.
-
#
-
# strip_links('<a href="http://www.rubyonrails.org">Ruby on Rails</a>')
-
# # => Ruby on Rails
-
#
-
# strip_links('Please e-mail me at <a href="mailto:me@email.com">me@email.com</a>.')
-
# # => Please e-mail me at me@email.com.
-
#
-
# strip_links('Blog: <a href="http://www.myblog.com/" class="nav" target=\"_blank\">Visit</a>.')
-
# # => Blog: Visit.
-
1
def strip_links(html)
-
self.class.link_sanitizer.sanitize(html)
-
end
-
-
1
module ClassMethods #:nodoc:
-
1
attr_writer :full_sanitizer, :link_sanitizer, :white_list_sanitizer
-
-
1
def sanitized_protocol_separator
-
white_list_sanitizer.protocol_separator
-
end
-
-
1
def sanitized_uri_attributes
-
white_list_sanitizer.uri_attributes
-
end
-
-
1
def sanitized_bad_tags
-
white_list_sanitizer.bad_tags
-
end
-
-
1
def sanitized_allowed_tags
-
white_list_sanitizer.allowed_tags
-
end
-
-
1
def sanitized_allowed_attributes
-
white_list_sanitizer.allowed_attributes
-
end
-
-
1
def sanitized_allowed_css_properties
-
white_list_sanitizer.allowed_css_properties
-
end
-
-
1
def sanitized_allowed_css_keywords
-
white_list_sanitizer.allowed_css_keywords
-
end
-
-
1
def sanitized_shorthand_css_properties
-
white_list_sanitizer.shorthand_css_properties
-
end
-
-
1
def sanitized_allowed_protocols
-
white_list_sanitizer.allowed_protocols
-
end
-
-
1
def sanitized_protocol_separator=(value)
-
white_list_sanitizer.protocol_separator = value
-
end
-
-
# Gets the HTML::FullSanitizer instance used by +strip_tags+. Replace with
-
# any object that responds to +sanitize+.
-
#
-
# class Application < Rails::Application
-
# config.action_view.full_sanitizer = MySpecialSanitizer.new
-
# end
-
#
-
1
def full_sanitizer
-
@full_sanitizer ||= HTML::FullSanitizer.new
-
end
-
-
# Gets the HTML::LinkSanitizer instance used by +strip_links+. Replace with
-
# any object that responds to +sanitize+.
-
#
-
# class Application < Rails::Application
-
# config.action_view.link_sanitizer = MySpecialSanitizer.new
-
# end
-
#
-
1
def link_sanitizer
-
@link_sanitizer ||= HTML::LinkSanitizer.new
-
end
-
-
# Gets the HTML::WhiteListSanitizer instance used by sanitize and +sanitize_css+.
-
# Replace with any object that responds to +sanitize+.
-
#
-
# class Application < Rails::Application
-
# config.action_view.white_list_sanitizer = MySpecialSanitizer.new
-
# end
-
#
-
1
def white_list_sanitizer
-
@white_list_sanitizer ||= HTML::WhiteListSanitizer.new
-
end
-
-
# Adds valid HTML attributes that the +sanitize+ helper checks for URIs.
-
#
-
# class Application < Rails::Application
-
# config.action_view.sanitized_uri_attributes = 'lowsrc', 'target'
-
# end
-
#
-
1
def sanitized_uri_attributes=(attributes)
-
HTML::WhiteListSanitizer.uri_attributes.merge(attributes)
-
end
-
-
# Adds to the Set of 'bad' tags for the +sanitize+ helper.
-
#
-
# class Application < Rails::Application
-
# config.action_view.sanitized_bad_tags = 'embed', 'object'
-
# end
-
#
-
1
def sanitized_bad_tags=(attributes)
-
HTML::WhiteListSanitizer.bad_tags.merge(attributes)
-
end
-
-
# Adds to the Set of allowed tags for the +sanitize+ helper.
-
#
-
# class Application < Rails::Application
-
# config.action_view.sanitized_allowed_tags = 'table', 'tr', 'td'
-
# end
-
#
-
1
def sanitized_allowed_tags=(attributes)
-
HTML::WhiteListSanitizer.allowed_tags.merge(attributes)
-
end
-
-
# Adds to the Set of allowed HTML attributes for the +sanitize+ helper.
-
#
-
# class Application < Rails::Application
-
# config.action_view.sanitized_allowed_attributes = 'onclick', 'longdesc'
-
# end
-
#
-
1
def sanitized_allowed_attributes=(attributes)
-
HTML::WhiteListSanitizer.allowed_attributes.merge(attributes)
-
end
-
-
# Adds to the Set of allowed CSS properties for the #sanitize and +sanitize_css+ helpers.
-
#
-
# class Application < Rails::Application
-
# config.action_view.sanitized_allowed_css_properties = 'expression'
-
# end
-
#
-
1
def sanitized_allowed_css_properties=(attributes)
-
HTML::WhiteListSanitizer.allowed_css_properties.merge(attributes)
-
end
-
-
# Adds to the Set of allowed CSS keywords for the +sanitize+ and +sanitize_css+ helpers.
-
#
-
# class Application < Rails::Application
-
# config.action_view.sanitized_allowed_css_keywords = 'expression'
-
# end
-
#
-
1
def sanitized_allowed_css_keywords=(attributes)
-
HTML::WhiteListSanitizer.allowed_css_keywords.merge(attributes)
-
end
-
-
# Adds to the Set of allowed shorthand CSS properties for the +sanitize+ and +sanitize_css+ helpers.
-
#
-
# class Application < Rails::Application
-
# config.action_view.sanitized_shorthand_css_properties = 'expression'
-
# end
-
#
-
1
def sanitized_shorthand_css_properties=(attributes)
-
HTML::WhiteListSanitizer.shorthand_css_properties.merge(attributes)
-
end
-
-
# Adds to the Set of allowed protocols for the +sanitize+ helper.
-
#
-
# class Application < Rails::Application
-
# config.action_view.sanitized_allowed_protocols = 'ssh', 'feed'
-
# end
-
#
-
1
def sanitized_allowed_protocols=(attributes)
-
HTML::WhiteListSanitizer.allowed_protocols.merge(attributes)
-
end
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/string/output_safety'
-
1
require 'set'
-
-
1
module ActionView
-
# = Action View Tag Helpers
-
1
module Helpers #:nodoc:
-
# Provides methods to generate HTML tags programmatically when you can't use
-
# a Builder. By default, they output XHTML compliant tags.
-
1
module TagHelper
-
1
extend ActiveSupport::Concern
-
1
include CaptureHelper
-
-
1
BOOLEAN_ATTRIBUTES = %w(disabled readonly multiple checked autobuffer
-
autoplay controls loop selected hidden scoped async
-
defer reversed ismap seemless muted required
-
autofocus novalidate formnovalidate open pubdate itemscope).to_set
-
25
BOOLEAN_ATTRIBUTES.merge(BOOLEAN_ATTRIBUTES.map {|attribute| attribute.to_sym })
-
-
1
PRE_CONTENT_STRINGS = {
-
:textarea => "\n"
-
}
-
-
# Returns an empty HTML tag of type +name+ which by default is XHTML
-
# compliant. Set +open+ to true to create an open tag compatible
-
# with HTML 4.0 and below. Add HTML attributes by passing an attributes
-
# hash to +options+. Set +escape+ to false to disable attribute value
-
# escaping.
-
#
-
# ==== Options
-
# You can use symbols or strings for the attribute names.
-
#
-
# Use +true+ with boolean attributes that can render with no value, like
-
# +disabled+ and +readonly+.
-
#
-
# HTML5 <tt>data-*</tt> attributes can be set with a single +data+ key
-
# pointing to a hash of sub-attributes.
-
#
-
# To play nicely with JavaScript conventions sub-attributes are dasherized.
-
# For example, a key +user_id+ would render as <tt>data-user-id</tt> and
-
# thus accessed as <tt>dataset.userId</tt>.
-
#
-
# Values are encoded to JSON, with the exception of strings and symbols.
-
# This may come in handy when using jQuery's HTML5-aware <tt>.data()</tt>
-
# from 1.4.3.
-
#
-
# ==== Examples
-
# tag("br")
-
# # => <br />
-
#
-
# tag("br", nil, true)
-
# # => <br>
-
#
-
# tag("input", type: 'text', disabled: true)
-
# # => <input type="text" disabled="disabled" />
-
#
-
# tag("img", src: "open & shut.png")
-
# # => <img src="open & shut.png" />
-
#
-
# tag("img", {src: "open & shut.png"}, false, false)
-
# # => <img src="open & shut.png" />
-
#
-
# tag("div", data: {name: 'Stephen', city_state: %w(Chicago IL)})
-
# # => <div data-name="Stephen" data-city-state="["Chicago","IL"]" />
-
1
def tag(name, options = nil, open = false, escape = true)
-
8
"<#{name}#{tag_options(options, escape) if options}#{open ? ">" : " />"}".html_safe
-
end
-
-
# Returns an HTML block tag of type +name+ surrounding the +content+. Add
-
# HTML attributes by passing an attributes hash to +options+.
-
# Instead of passing the content as an argument, you can also use a block
-
# in which case, you pass your +options+ as the second parameter.
-
# Set escape to false to disable attribute value escaping.
-
#
-
# ==== Options
-
# The +options+ hash is used with attributes with no value like (<tt>disabled</tt> and
-
# <tt>readonly</tt>), which you can give a value of true in the +options+ hash. You can use
-
# symbols or strings for the attribute names.
-
#
-
# ==== Examples
-
# content_tag(:p, "Hello world!")
-
# # => <p>Hello world!</p>
-
# content_tag(:div, content_tag(:p, "Hello world!"), class: "strong")
-
# # => <div class="strong"><p>Hello world!</p></div>
-
# content_tag("select", options, multiple: true)
-
# # => <select multiple="multiple">...options...</select>
-
#
-
# <%= content_tag :div, class: "strong" do -%>
-
# Hello world!
-
# <% end -%>
-
# # => <div class="strong">Hello world!</div>
-
1
def content_tag(name, content_or_options_with_block = nil, options = nil, escape = true, &block)
-
if block_given?
-
options = content_or_options_with_block if content_or_options_with_block.is_a?(Hash)
-
content_tag_string(name, capture(&block), options, escape)
-
else
-
content_tag_string(name, content_or_options_with_block, options, escape)
-
end
-
end
-
-
# Returns a CDATA section with the given +content+. CDATA sections
-
# are used to escape blocks of text containing characters which would
-
# otherwise be recognized as markup. CDATA sections begin with the string
-
# <tt><![CDATA[</tt> and end with (and may not contain) the string <tt>]]></tt>.
-
#
-
# cdata_section("<hello world>")
-
# # => <![CDATA[<hello world>]]>
-
#
-
# cdata_section(File.read("hello_world.txt"))
-
# # => <![CDATA[<hello from a text file]]>
-
#
-
# cdata_section("hello]]>world")
-
# # => <![CDATA[hello]]]]><![CDATA[>world]]>
-
1
def cdata_section(content)
-
splitted = content.gsub(']]>', ']]]]><![CDATA[>')
-
"<![CDATA[#{splitted}]]>".html_safe
-
end
-
-
# Returns an escaped version of +html+ without affecting existing escaped entities.
-
#
-
# escape_once("1 < 2 & 3")
-
# # => "1 < 2 & 3"
-
#
-
# escape_once("<< Accept & Checkout")
-
# # => "<< Accept & Checkout"
-
1
def escape_once(html)
-
ERB::Util.html_escape_once(html)
-
end
-
-
1
private
-
-
1
def content_tag_string(name, content, options, escape = true)
-
tag_options = tag_options(options, escape) if options
-
content = ERB::Util.h(content) if escape
-
"<#{name}#{tag_options}>#{PRE_CONTENT_STRINGS[name.to_sym]}#{content}</#{name}>".html_safe
-
end
-
-
1
def tag_options(options, escape = true)
-
8
return if options.blank?
-
8
attrs = []
-
8
options.each_pair do |key, value|
-
15
if key.to_s == 'data' && value.is_a?(Hash)
-
value.each_pair do |k, v|
-
attrs << data_tag_option(k, v, escape)
-
end
-
elsif BOOLEAN_ATTRIBUTES.include?(key)
-
attrs << boolean_tag_option(key) if value
-
elsif !value.nil?
-
15
attrs << tag_option(key, value, escape)
-
end
-
end
-
8
" #{attrs.sort * ' '}".html_safe unless attrs.empty?
-
end
-
-
1
def data_tag_option(key, value, escape)
-
key = "data-#{key.to_s.dasherize}"
-
unless value.is_a?(String) || value.is_a?(Symbol) || value.is_a?(BigDecimal)
-
value = value.to_json
-
end
-
tag_option(key, value, escape)
-
end
-
-
1
def boolean_tag_option(key)
-
%(#{key}="#{key}")
-
end
-
-
1
def tag_option(key, value, escape)
-
15
value = value.join(" ") if value.is_a?(Array)
-
15
value = ERB::Util.h(value) if escape
-
15
%(#{key}="#{value}")
-
end
-
end
-
end
-
end
-
1
module ActionView
-
1
module Helpers
-
1
module Tags #:nodoc:
-
1
extend ActiveSupport::Autoload
-
-
1
autoload :Base
-
1
autoload :CheckBox
-
1
autoload :CollectionCheckBoxes
-
1
autoload :CollectionRadioButtons
-
1
autoload :CollectionSelect
-
1
autoload :ColorField
-
1
autoload :DateField
-
1
autoload :DateSelect
-
1
autoload :DatetimeField
-
1
autoload :DatetimeLocalField
-
1
autoload :DatetimeSelect
-
1
autoload :EmailField
-
1
autoload :FileField
-
1
autoload :GroupedCollectionSelect
-
1
autoload :HiddenField
-
1
autoload :Label
-
1
autoload :MonthField
-
1
autoload :NumberField
-
1
autoload :PasswordField
-
1
autoload :RadioButton
-
1
autoload :RangeField
-
1
autoload :SearchField
-
1
autoload :Select
-
1
autoload :TelField
-
1
autoload :TextArea
-
1
autoload :TextField
-
1
autoload :TimeField
-
1
autoload :TimeSelect
-
1
autoload :TimeZoneSelect
-
1
autoload :UrlField
-
1
autoload :WeekField
-
end
-
end
-
end
-
1
require 'active_support/core_ext/string/filters'
-
1
require 'active_support/core_ext/array/extract_options'
-
-
1
module ActionView
-
# = Action View Text Helpers
-
1
module Helpers #:nodoc:
-
# The TextHelper module provides a set of methods for filtering, formatting
-
# and transforming strings, which can reduce the amount of inline Ruby code in
-
# your views. These helper methods extend Action View making them callable
-
# within your template files.
-
#
-
# ==== Sanitization
-
#
-
# Most text helpers by default sanitize the given content, but do not escape it.
-
# This means HTML tags will appear in the page but all malicious code will be removed.
-
# Let's look at some examples using the +simple_format+ method:
-
#
-
# simple_format('<a href="http://example.com/">Example</a>')
-
# # => "<p><a href=\"http://example.com/\">Example</a></p>"
-
#
-
# simple_format('<a href="javascript:alert(\'no!\')">Example</a>')
-
# # => "<p><a>Example</a></p>"
-
#
-
# If you want to escape all content, you should invoke the +h+ method before
-
# calling the text helper.
-
#
-
# simple_format h('<a href="http://example.com/">Example</a>')
-
# # => "<p><a href=\"http://example.com/\">Example</a></p>"
-
1
module TextHelper
-
1
extend ActiveSupport::Concern
-
-
1
include SanitizeHelper
-
1
include TagHelper
-
# The preferred method of outputting text in your views is to use the
-
# <%= "text" %> eRuby syntax. The regular _puts_ and _print_ methods
-
# do not operate as expected in an eRuby code block. If you absolutely must
-
# output text within a non-output code block (i.e., <% %>), you can use the concat method.
-
#
-
# <%
-
# concat "hello"
-
# # is the equivalent of <%= "hello" %>
-
#
-
# if logged_in
-
# concat "Logged in!"
-
# else
-
# concat link_to('login', action: :login)
-
# end
-
# # will either display "Logged in!" or a login link
-
# %>
-
1
def concat(string)
-
output_buffer << string
-
end
-
-
1
def safe_concat(string)
-
output_buffer.respond_to?(:safe_concat) ? output_buffer.safe_concat(string) : concat(string)
-
end
-
-
# Truncates a given +text+ after a given <tt>:length</tt> if +text+ is longer than <tt>:length</tt>
-
# (defaults to 30). The last characters will be replaced with the <tt>:omission</tt> (defaults to "...")
-
# for a total length not exceeding <tt>:length</tt>.
-
#
-
# Pass a <tt>:separator</tt> to truncate +text+ at a natural break.
-
#
-
# Pass a block if you want to show extra content when the text is truncated.
-
#
-
# The result is marked as HTML-safe, but it is escaped by default, unless <tt>:escape</tt> is
-
# +false+. Care should be taken if +text+ contains HTML tags or entities, because truncation
-
# may produce invalid HTML (such as unbalanced or incomplete tags).
-
#
-
# truncate("Once upon a time in a world far far away")
-
# # => "Once upon a time in a world..."
-
#
-
# truncate("Once upon a time in a world far far away", length: 17)
-
# # => "Once upon a ti..."
-
#
-
# truncate("Once upon a time in a world far far away", length: 17, separator: ' ')
-
# # => "Once upon a..."
-
#
-
# truncate("And they found that many people were sleeping better.", length: 25, omission: '... (continued)')
-
# # => "And they f... (continued)"
-
#
-
# truncate("<p>Once upon a time in a world far far away</p>")
-
# # => "<p>Once upon a time in a wo..."
-
#
-
# truncate("Once upon a time in a world far far away") { link_to "Continue", "#" }
-
# # => "Once upon a time in a wo...<a href="#">Continue</a>"
-
1
def truncate(text, options = {}, &block)
-
if text
-
length = options.fetch(:length, 30)
-
-
content = text.truncate(length, options)
-
content = options[:escape] == false ? content.html_safe : ERB::Util.html_escape(content)
-
content << capture(&block) if block_given? && text.length > length
-
content
-
end
-
end
-
-
# Highlights one or more +phrases+ everywhere in +text+ by inserting it into
-
# a <tt>:highlighter</tt> string. The highlighter can be specialized by passing <tt>:highlighter</tt>
-
# as a single-quoted string with <tt>\1</tt> where the phrase is to be inserted (defaults to
-
# '<mark>\1</mark>')
-
#
-
# highlight('You searched for: rails', 'rails')
-
# # => You searched for: <mark>rails</mark>
-
#
-
# highlight('You searched for: ruby, rails, dhh', 'actionpack')
-
# # => You searched for: ruby, rails, dhh
-
#
-
# highlight('You searched for: rails', ['for', 'rails'], highlighter: '<em>\1</em>')
-
# # => You searched <em>for</em>: <em>rails</em>
-
#
-
# highlight('You searched for: rails', 'rails', highlighter: '<a href="search?q=\1">\1</a>')
-
# # => You searched for: <a href="search?q=rails">rails</a>
-
1
def highlight(text, phrases, options = {})
-
highlighter = options.fetch(:highlighter, '<mark>\1</mark>')
-
-
text = sanitize(text) if options.fetch(:sanitize, true)
-
if text.blank? || phrases.blank?
-
text
-
else
-
match = Array(phrases).map { |p| Regexp.escape(p) }.join('|')
-
text.gsub(/(#{match})(?![^<]*?>)/i, highlighter)
-
end.html_safe
-
end
-
-
# Extracts an excerpt from +text+ that matches the first instance of +phrase+.
-
# The <tt>:radius</tt> option expands the excerpt on each side of the first occurrence of +phrase+ by the number of characters
-
# defined in <tt>:radius</tt> (which defaults to 100). If the excerpt radius overflows the beginning or end of the +text+,
-
# then the <tt>:omission</tt> option (which defaults to "...") will be prepended/appended accordingly. The
-
# <tt>:separator</tt> enable to choose the delimation. The resulting string will be stripped in any case. If the +phrase+
-
# isn't found, nil is returned.
-
#
-
# excerpt('This is an example', 'an', radius: 5)
-
# # => ...s is an exam...
-
#
-
# excerpt('This is an example', 'is', radius: 5)
-
# # => This is a...
-
#
-
# excerpt('This is an example', 'is')
-
# # => This is an example
-
#
-
# excerpt('This next thing is an example', 'ex', radius: 2)
-
# # => ...next...
-
#
-
# excerpt('This is also an example', 'an', radius: 8, omission: '<chop> ')
-
# # => <chop> is also an example
-
#
-
# excerpt('This is a very beautiful morning', 'very', separator: ' ', radius: 1)
-
# # => ...a very beautiful...
-
1
def excerpt(text, phrase, options = {})
-
return unless text && phrase
-
-
separator = options.fetch(:separator, "")
-
phrase = Regexp.escape(phrase)
-
regex = /#{phrase}/i
-
-
return unless matches = text.match(regex)
-
phrase = matches[0]
-
-
text.split(separator).each do |value|
-
if value.match(regex)
-
regex = phrase = value
-
break
-
end
-
end
-
-
first_part, second_part = text.split(regex, 2)
-
-
prefix, first_part = cut_excerpt_part(:first, first_part, separator, options)
-
postfix, second_part = cut_excerpt_part(:second, second_part, separator, options)
-
-
prefix + (first_part + separator + phrase + separator + second_part).strip + postfix
-
end
-
-
# Attempts to pluralize the +singular+ word unless +count+ is 1. If
-
# +plural+ is supplied, it will use that when count is > 1, otherwise
-
# it will use the Inflector to determine the plural form.
-
#
-
# pluralize(1, 'person')
-
# # => 1 person
-
#
-
# pluralize(2, 'person')
-
# # => 2 people
-
#
-
# pluralize(3, 'person', 'users')
-
# # => 3 users
-
#
-
# pluralize(0, 'person')
-
# # => 0 people
-
1
def pluralize(count, singular, plural = nil)
-
word = if (count == 1 || count =~ /^1(\.0+)?$/)
-
singular
-
else
-
plural || singular.pluralize
-
end
-
-
"#{count || 0} #{word}"
-
end
-
-
# Wraps the +text+ into lines no longer than +line_width+ width. This method
-
# breaks on the first whitespace character that does not exceed +line_width+
-
# (which is 80 by default).
-
#
-
# word_wrap('Once upon a time')
-
# # => Once upon a time
-
#
-
# word_wrap('Once upon a time, in a kingdom called Far Far Away, a king fell ill, and finding a successor to the throne turned out to be more trouble than anyone could have imagined...')
-
# # => Once upon a time, in a kingdom called Far Far Away, a king fell ill, and finding\na successor to the throne turned out to be more trouble than anyone could have\nimagined...
-
#
-
# word_wrap('Once upon a time', line_width: 8)
-
# # => Once\nupon a\ntime
-
#
-
# word_wrap('Once upon a time', line_width: 1)
-
# # => Once\nupon\na\ntime
-
1
def word_wrap(text, options = {})
-
line_width = options.fetch(:line_width, 80)
-
-
text.split("\n").collect do |line|
-
line.length > line_width ? line.gsub(/(.{1,#{line_width}})(\s+|$)/, "\\1\n").strip : line
-
end * "\n"
-
end
-
-
# Returns +text+ transformed into HTML using simple formatting rules.
-
# Two or more consecutive newlines(<tt>\n\n</tt>) are considered as a
-
# paragraph and wrapped in <tt><p></tt> tags. One newline (<tt>\n</tt>) is
-
# considered as a linebreak and a <tt><br /></tt> tag is appended. This
-
# method does not remove the newlines from the +text+.
-
#
-
# You can pass any HTML attributes into <tt>html_options</tt>. These
-
# will be added to all created paragraphs.
-
#
-
# ==== Options
-
# * <tt>:sanitize</tt> - If +false+, does not sanitize +text+.
-
# * <tt>:wrapper_tag</tt> - String representing the wrapper tag, defaults to <tt>"p"</tt>
-
#
-
# ==== Examples
-
# my_text = "Here is some basic text...\n...with a line break."
-
#
-
# simple_format(my_text)
-
# # => "<p>Here is some basic text...\n<br />...with a line break.</p>"
-
#
-
# simple_format(my_text, {}, wrapper_tag: "div")
-
# # => "<div>Here is some basic text...\n<br />...with a line break.</div>"
-
#
-
# more_text = "We want to put a paragraph...\n\n...right there."
-
#
-
# simple_format(more_text)
-
# # => "<p>We want to put a paragraph...</p>\n\n<p>...right there.</p>"
-
#
-
# simple_format("Look ma! A class!", class: 'description')
-
# # => "<p class='description'>Look ma! A class!</p>"
-
#
-
# simple_format("<span>I'm allowed!</span> It's true.", {}, sanitize: false)
-
# # => "<p><span>I'm allowed!</span> It's true.</p>"
-
1
def simple_format(text, html_options = {}, options = {})
-
wrapper_tag = options.fetch(:wrapper_tag, :p)
-
-
text = sanitize(text) if options.fetch(:sanitize, true)
-
paragraphs = split_paragraphs(text)
-
-
if paragraphs.empty?
-
content_tag(wrapper_tag, nil, html_options)
-
else
-
paragraphs.map { |paragraph|
-
content_tag(wrapper_tag, paragraph, html_options, options[:sanitize])
-
}.join("\n\n").html_safe
-
end
-
end
-
-
# Creates a Cycle object whose _to_s_ method cycles through elements of an
-
# array every time it is called. This can be used for example, to alternate
-
# classes for table rows. You can use named cycles to allow nesting in loops.
-
# Passing a Hash as the last parameter with a <tt>:name</tt> key will create a
-
# named cycle. The default name for a cycle without a +:name+ key is
-
# <tt>"default"</tt>. You can manually reset a cycle by calling reset_cycle
-
# and passing the name of the cycle. The current cycle string can be obtained
-
# anytime using the current_cycle method.
-
#
-
# # Alternate CSS classes for even and odd numbers...
-
# @items = [1,2,3,4]
-
# <table>
-
# <% @items.each do |item| %>
-
# <tr class="<%= cycle("odd", "even") -%>">
-
# <td>item</td>
-
# </tr>
-
# <% end %>
-
# </table>
-
#
-
#
-
# # Cycle CSS classes for rows, and text colors for values within each row
-
# @items = x = [{first: 'Robert', middle: 'Daniel', last: 'James'},
-
# {first: 'Emily', middle: 'Shannon', maiden: 'Pike', last: 'Hicks'},
-
# {first: 'June', middle: 'Dae', last: 'Jones'}]
-
# <% @items.each do |item| %>
-
# <tr class="<%= cycle("odd", "even", name: "row_class") -%>">
-
# <td>
-
# <% item.values.each do |value| %>
-
# <%# Create a named cycle "colors" %>
-
# <span style="color:<%= cycle("red", "green", "blue", name: "colors") -%>">
-
# <%= value %>
-
# </span>
-
# <% end %>
-
# <% reset_cycle("colors") %>
-
# </td>
-
# </tr>
-
# <% end %>
-
1
def cycle(first_value, *values)
-
options = values.extract_options!
-
name = options.fetch(:name, 'default')
-
-
values.unshift(first_value)
-
-
cycle = get_cycle(name)
-
unless cycle && cycle.values == values
-
cycle = set_cycle(name, Cycle.new(*values))
-
end
-
cycle.to_s
-
end
-
-
# Returns the current cycle string after a cycle has been started. Useful
-
# for complex table highlighting or any other design need which requires
-
# the current cycle string in more than one place.
-
#
-
# # Alternate background colors
-
# @items = [1,2,3,4]
-
# <% @items.each do |item| %>
-
# <div style="background-color:<%= cycle("red","white","blue") %>">
-
# <span style="background-color:<%= current_cycle %>"><%= item %></span>
-
# </div>
-
# <% end %>
-
1
def current_cycle(name = "default")
-
cycle = get_cycle(name)
-
cycle.current_value if cycle
-
end
-
-
# Resets a cycle so that it starts from the first element the next time
-
# it is called. Pass in +name+ to reset a named cycle.
-
#
-
# # Alternate CSS classes for even and odd numbers...
-
# @items = [[1,2,3,4], [5,6,3], [3,4,5,6,7,4]]
-
# <table>
-
# <% @items.each do |item| %>
-
# <tr class="<%= cycle("even", "odd") -%>">
-
# <% item.each do |value| %>
-
# <span style="color:<%= cycle("#333", "#666", "#999", name: "colors") -%>">
-
# <%= value %>
-
# </span>
-
# <% end %>
-
#
-
# <% reset_cycle("colors") %>
-
# </tr>
-
# <% end %>
-
# </table>
-
1
def reset_cycle(name = "default")
-
cycle = get_cycle(name)
-
cycle.reset if cycle
-
end
-
-
1
class Cycle #:nodoc:
-
1
attr_reader :values
-
-
1
def initialize(first_value, *values)
-
@values = values.unshift(first_value)
-
reset
-
end
-
-
1
def reset
-
@index = 0
-
end
-
-
1
def current_value
-
@values[previous_index].to_s
-
end
-
-
1
def to_s
-
value = @values[@index].to_s
-
@index = next_index
-
return value
-
end
-
-
1
private
-
-
1
def next_index
-
step_index(1)
-
end
-
-
1
def previous_index
-
step_index(-1)
-
end
-
-
1
def step_index(n)
-
(@index + n) % @values.size
-
end
-
end
-
-
1
private
-
# The cycle helpers need to store the cycles in a place that is
-
# guaranteed to be reset every time a page is rendered, so it
-
# uses an instance variable of ActionView::Base.
-
1
def get_cycle(name)
-
@_cycles = Hash.new unless defined?(@_cycles)
-
return @_cycles[name]
-
end
-
-
1
def set_cycle(name, cycle_object)
-
@_cycles = Hash.new unless defined?(@_cycles)
-
@_cycles[name] = cycle_object
-
end
-
-
1
def split_paragraphs(text)
-
return [] if text.blank?
-
-
text.to_str.gsub(/\r\n?/, "\n").split(/\n\n+/).map! do |t|
-
t.gsub!(/([^\n]\n)(?=[^\n])/, '\1<br />') || t
-
end
-
end
-
-
1
def cut_excerpt_part(part_position, part, separator, options)
-
return "", "" unless part
-
-
radius = options.fetch(:radius, 100)
-
omission = options.fetch(:omission, "...")
-
-
part = part.split(separator)
-
part.delete("")
-
affix = part.size > radius ? omission : ""
-
-
part = if part_position == :first
-
drop_index = [part.length - radius, 0].max
-
part.drop(drop_index)
-
else
-
part.first(radius)
-
end
-
-
return affix, part.join(separator)
-
end
-
end
-
end
-
end
-
1
require 'action_view/helpers/tag_helper'
-
1
require 'i18n/exceptions'
-
-
1
module I18n
-
1
class ExceptionHandler
-
include Module.new {
-
1
def call(exception, locale, key, options)
-
exception.is_a?(MissingTranslation) && options[:rescue_format] == :html ? super.html_safe : super
-
end
-
1
}
-
end
-
end
-
-
1
module ActionView
-
# = Action View Translation Helpers
-
1
module Helpers
-
1
module TranslationHelper
-
# Delegates to <tt>I18n#translate</tt> but also performs three additional functions.
-
#
-
# First, it'll pass the <tt>rescue_format: :html</tt> option to I18n so that any
-
# thrown +MissingTranslation+ messages will be turned into inline spans that
-
#
-
# * have a "translation-missing" class set,
-
# * contain the missing key as a title attribute and
-
# * a titleized version of the last key segment as a text.
-
#
-
# E.g. the value returned for a missing translation key :"blog.post.title" will be
-
# <span class="translation_missing" title="translation missing: en.blog.post.title">Title</span>.
-
# This way your views will display rather reasonable strings but it will still
-
# be easy to spot missing translations.
-
#
-
# Second, it'll scope the key by the current partial if the key starts
-
# with a period. So if you call <tt>translate(".foo")</tt> from the
-
# <tt>people/index.html.erb</tt> template, you'll actually be calling
-
# <tt>I18n.translate("people.index.foo")</tt>. This makes it less repetitive
-
# to translate many keys within the same partials and gives you a simple framework
-
# for scoping them consistently. If you don't prepend the key with a period,
-
# nothing is converted.
-
#
-
# Third, it'll mark the translation as safe HTML if the key has the suffix
-
# "_html" or the last element of the key is the word "html". For example,
-
# calling translate("footer_html") or translate("footer.html") will return
-
# a safe HTML string that won't be escaped by other HTML helper methods. This
-
# naming convention helps to identify translations that include HTML tags so that
-
# you know what kind of output to expect when you call translate in a template.
-
1
def translate(key, options = {})
-
1
options.merge!(:rescue_format => :html) unless options.key?(:rescue_format)
-
1
options[:default] = wrap_translate_defaults(options[:default]) if options[:default]
-
1
if html_safe_translation_key?(key)
-
html_safe_options = options.dup
-
options.except(*I18n::RESERVED_KEYS).each do |name, value|
-
unless name == :count && value.is_a?(Numeric)
-
html_safe_options[name] = ERB::Util.html_escape(value.to_s)
-
end
-
end
-
translation = I18n.translate(scope_key_by_partial(key), html_safe_options)
-
-
translation.respond_to?(:html_safe) ? translation.html_safe : translation
-
else
-
1
I18n.translate(scope_key_by_partial(key), options)
-
end
-
end
-
1
alias :t :translate
-
-
# Delegates to <tt>I18n.localize</tt> with no additional functionality.
-
#
-
# See http://rubydoc.info/github/svenfuchs/i18n/master/I18n/Backend/Base:localize
-
# for more information.
-
1
def localize(*args)
-
I18n.localize(*args)
-
end
-
1
alias :l :localize
-
-
1
private
-
1
def scope_key_by_partial(key)
-
1
if key.to_s.first == "."
-
1
if @virtual_path
-
1
@virtual_path.gsub(%r{/_?}, ".") + key.to_s
-
else
-
raise "Cannot use t(#{key.inspect}) shortcut because path is not available"
-
end
-
else
-
key
-
end
-
end
-
-
1
def html_safe_translation_key?(key)
-
1
key.to_s =~ /(\b|_|\.)html$/
-
end
-
-
1
def wrap_translate_defaults(defaults)
-
new_defaults = []
-
defaults = Array(defaults)
-
while key = defaults.shift
-
if key.is_a?(Symbol)
-
new_defaults << lambda { |_, options| translate key, options.merge(:default => defaults) }
-
break
-
else
-
new_defaults << key
-
end
-
end
-
-
new_defaults
-
end
-
end
-
end
-
end
-
1
require 'action_view/helpers/javascript_helper'
-
1
require 'active_support/core_ext/array/access'
-
1
require 'active_support/core_ext/hash/keys'
-
1
require 'active_support/core_ext/string/output_safety'
-
-
1
module ActionView
-
# = Action View URL Helpers
-
1
module Helpers #:nodoc:
-
# Provides a set of methods for making links and getting URLs that
-
# depend on the routing subsystem (see ActionDispatch::Routing).
-
# This allows you to use the same format for links in views
-
# and controllers.
-
1
module UrlHelper
-
# This helper may be included in any class that includes the
-
# URL helpers of a routes (routes.url_helpers). Some methods
-
# provided here will only work in the context of a request
-
# (link_to_unless_current, for instance), which must be provided
-
# as a method called #request on the context.
-
-
1
extend ActiveSupport::Concern
-
-
1
include TagHelper
-
-
1
module ClassMethods
-
1
def _url_for_modules
-
16
ActionView::RoutingUrlFor
-
end
-
end
-
-
# Basic implementation of url_for to allow use helpers without routes existence
-
1
def url_for(options = nil) # :nodoc:
-
case options
-
when String
-
options
-
when :back
-
_back_url
-
else
-
raise ArgumentError, "arguments passed to url_for can't be handled. Please require " +
-
"routes or provide your own implementation"
-
end
-
end
-
-
1
def _back_url # :nodoc:
-
referrer = controller.respond_to?(:request) && controller.request.env["HTTP_REFERER"]
-
referrer || 'javascript:history.back()'
-
end
-
1
protected :_back_url
-
-
# Creates a link tag of the given +name+ using a URL created by the set of +options+.
-
# See the valid options in the documentation for +url_for+. It's also possible to
-
# pass a String instead of an options hash, which generates a link tag that uses the
-
# value of the String as the href for the link. Using a <tt>:back</tt> Symbol instead
-
# of an options hash will generate a link to the referrer (a JavaScript back link
-
# will be used in place of a referrer if none exists). If +nil+ is passed as the name
-
# the value of the link itself will become the name.
-
#
-
# ==== Signatures
-
#
-
# link_to(body, url, html_options = {})
-
# # url is a String; you can use URL helpers like
-
# # posts_path
-
#
-
# link_to(body, url_options = {}, html_options = {})
-
# # url_options, except :method, is passed to url_for
-
#
-
# link_to(options = {}, html_options = {}) do
-
# # name
-
# end
-
#
-
# link_to(url, html_options = {}) do
-
# # name
-
# end
-
#
-
# ==== Options
-
# * <tt>:data</tt> - This option can be used to add custom data attributes.
-
# * <tt>method: symbol of HTTP verb</tt> - This modifier will dynamically
-
# create an HTML form and immediately submit the form for processing using
-
# the HTTP verb specified. Useful for having links perform a POST operation
-
# in dangerous actions like deleting a record (which search bots can follow
-
# while spidering your site). Supported verbs are <tt>:post</tt>, <tt>:delete</tt>, <tt>:patch</tt>, and <tt>:put</tt>.
-
# Note that if the user has JavaScript disabled, the request will fall back
-
# to using GET. If <tt>href: '#'</tt> is used and the user has JavaScript
-
# disabled clicking the link will have no effect. If you are relying on the
-
# POST behavior, you should check for it in your controller's action by using
-
# the request object's methods for <tt>post?</tt>, <tt>delete?</tt>, <tt>:patch</tt>, or <tt>put?</tt>.
-
# * <tt>remote: true</tt> - This will allow the unobtrusive JavaScript
-
# driver to make an Ajax request to the URL in question instead of following
-
# the link. The drivers each provide mechanisms for listening for the
-
# completion of the Ajax request and performing JavaScript operations once
-
# they're complete
-
#
-
# ==== Data attributes
-
#
-
# * <tt>confirm: 'question?'</tt> - This will allow the unobtrusive JavaScript
-
# driver to prompt with the question specified. If the user accepts, the link is
-
# processed normally, otherwise no action is taken.
-
# * <tt>:disable_with</tt> - Value of this parameter will be
-
# used as the value for a disabled version of the submit
-
# button when the form is submitted. This feature is provided
-
# by the unobtrusive JavaScript driver.
-
#
-
# ==== Examples
-
# Because it relies on +url_for+, +link_to+ supports both older-style controller/action/id arguments
-
# and newer RESTful routes. Current Rails style favors RESTful routes whenever possible, so base
-
# your application on resources and use
-
#
-
# link_to "Profile", profile_path(@profile)
-
# # => <a href="/profiles/1">Profile</a>
-
#
-
# or the even pithier
-
#
-
# link_to "Profile", @profile
-
# # => <a href="/profiles/1">Profile</a>
-
#
-
# in place of the older more verbose, non-resource-oriented
-
#
-
# link_to "Profile", controller: "profiles", action: "show", id: @profile
-
# # => <a href="/profiles/show/1">Profile</a>
-
#
-
# Similarly,
-
#
-
# link_to "Profiles", profiles_path
-
# # => <a href="/profiles">Profiles</a>
-
#
-
# is better than
-
#
-
# link_to "Profiles", controller: "profiles"
-
# # => <a href="/profiles">Profiles</a>
-
#
-
# You can use a block as well if your link target is hard to fit into the name parameter. ERB example:
-
#
-
# <%= link_to(@profile) do %>
-
# <strong><%= @profile.name %></strong> -- <span>Check it out!</span>
-
# <% end %>
-
# # => <a href="/profiles/1">
-
# <strong>David</strong> -- <span>Check it out!</span>
-
# </a>
-
#
-
# Classes and ids for CSS are easy to produce:
-
#
-
# link_to "Articles", articles_path, id: "news", class: "article"
-
# # => <a href="/articles" class="article" id="news">Articles</a>
-
#
-
# Be careful when using the older argument style, as an extra literal hash is needed:
-
#
-
# link_to "Articles", { controller: "articles" }, id: "news", class: "article"
-
# # => <a href="/articles" class="article" id="news">Articles</a>
-
#
-
# Leaving the hash off gives the wrong link:
-
#
-
# link_to "WRONG!", controller: "articles", id: "news", class: "article"
-
# # => <a href="/articles/index/news?class=article">WRONG!</a>
-
#
-
# +link_to+ can also produce links with anchors or query strings:
-
#
-
# link_to "Comment wall", profile_path(@profile, anchor: "wall")
-
# # => <a href="/profiles/1#wall">Comment wall</a>
-
#
-
# link_to "Ruby on Rails search", controller: "searches", query: "ruby on rails"
-
# # => <a href="/searches?query=ruby+on+rails">Ruby on Rails search</a>
-
#
-
# link_to "Nonsense search", searches_path(foo: "bar", baz: "quux")
-
# # => <a href="/searches?foo=bar&baz=quux">Nonsense search</a>
-
#
-
# The only option specific to +link_to+ (<tt>:method</tt>) is used as follows:
-
#
-
# link_to("Destroy", "http://www.example.com", method: :delete)
-
# # => <a href='http://www.example.com' rel="nofollow" data-method="delete">Destroy</a>
-
#
-
# You can also use custom data attributes using the <tt>:data</tt> option:
-
#
-
# link_to "Visit Other Site", "http://www.rubyonrails.org/", data: { confirm: "Are you sure?" }
-
# # => <a href="http://www.rubyonrails.org/" data-confirm="Are you sure?"">Visit Other Site</a>
-
1
def link_to(name = nil, options = nil, html_options = nil, &block)
-
html_options, options = options, name if block_given?
-
options ||= {}
-
-
html_options = convert_options_to_data_attributes(options, html_options)
-
-
url = url_for(options)
-
html_options['href'] ||= url
-
-
content_tag(:a, name || url, html_options, &block)
-
end
-
-
# Generates a form containing a single button that submits to the URL created
-
# by the set of +options+. This is the safest method to ensure links that
-
# cause changes to your data are not triggered by search bots or accelerators.
-
# If the HTML button does not work with your layout, you can also consider
-
# using the +link_to+ method with the <tt>:method</tt> modifier as described in
-
# the +link_to+ documentation.
-
#
-
# By default, the generated form element has a class name of <tt>button_to</tt>
-
# to allow styling of the form itself and its children. This can be changed
-
# using the <tt>:form_class</tt> modifier within +html_options+. You can control
-
# the form submission and input element behavior using +html_options+.
-
# This method accepts the <tt>:method</tt> modifier described in the +link_to+ documentation.
-
# If no <tt>:method</tt> modifier is given, it will default to performing a POST operation.
-
# You can also disable the button by passing <tt>disabled: true</tt> in +html_options+.
-
# If you are using RESTful routes, you can pass the <tt>:method</tt>
-
# to change the HTTP verb used to submit the form.
-
#
-
# ==== Options
-
# The +options+ hash accepts the same options as +url_for+.
-
#
-
# There are a few special +html_options+:
-
# * <tt>:method</tt> - Symbol of HTTP verb. Supported verbs are <tt>:post</tt>, <tt>:get</tt>,
-
# <tt>:delete</tt>, <tt>:patch</tt>, and <tt>:put</tt>. By default it will be <tt>:post</tt>.
-
# * <tt>:disabled</tt> - If set to true, it will generate a disabled button.
-
# * <tt>:data</tt> - This option can be used to add custom data attributes.
-
# * <tt>:remote</tt> - If set to true, will allow the Unobtrusive JavaScript drivers to control the
-
# submit behavior. By default this behavior is an ajax submit.
-
# * <tt>:form</tt> - This hash will be form attributes
-
# * <tt>:form_class</tt> - This controls the class of the form within which the submit button will
-
# be placed
-
#
-
# ==== Data attributes
-
#
-
# * <tt>:confirm</tt> - This will use the unobtrusive JavaScript driver to
-
# prompt with the question specified. If the user accepts, the link is
-
# processed normally, otherwise no action is taken.
-
# * <tt>:disable_with</tt> - Value of this parameter will be
-
# used as the value for a disabled version of the submit
-
# button when the form is submitted. This feature is provided
-
# by the unobtrusive JavaScript driver.
-
#
-
# ==== Examples
-
# <%= button_to "New", action: "new" %>
-
# # => "<form method="post" action="/controller/new" class="button_to">
-
# # <div><input value="New" type="submit" /></div>
-
# # </form>"
-
#
-
# <%= button_to [:make_happy, @user] do %>
-
# Make happy <strong><%= @user.name %></strong>
-
# <% end %>
-
# # => "<form method="post" action="/users/1/make_happy" class="button_to">
-
# # <div>
-
# # <button type="submit">
-
# # Make happy <strong><%= @user.name %></strong>
-
# # </button>
-
# # </div>
-
# # </form>"
-
#
-
# <%= button_to "New", action: "new", form_class: "new-thing" %>
-
# # => "<form method="post" action="/controller/new" class="new-thing">
-
# # <div><input value="New" type="submit" /></div>
-
# # </form>"
-
#
-
#
-
# <%= button_to "Create", action: "create", remote: true, form: { "data-type" => "json" } %>
-
# # => "<form method="post" action="/images/create" class="button_to" data-remote="true" data-type="json">
-
# # <div>
-
# # <input value="Create" type="submit" />
-
# # <input name="authenticity_token" type="hidden" value="10f2163b45388899ad4d5ae948988266befcb6c3d1b2451cf657a0c293d605a6"/>
-
# # </div>
-
# # </form>"
-
#
-
#
-
# <%= button_to "Delete Image", { action: "delete", id: @image.id },
-
# method: :delete, data: { confirm: "Are you sure?" } %>
-
# # => "<form method="post" action="/images/delete/1" class="button_to">
-
# # <div>
-
# # <input type="hidden" name="_method" value="delete" />
-
# # <input data-confirm='Are you sure?' value="Delete Image" type="submit" />
-
# # <input name="authenticity_token" type="hidden" value="10f2163b45388899ad4d5ae948988266befcb6c3d1b2451cf657a0c293d605a6"/>
-
# # </div>
-
# # </form>"
-
#
-
#
-
# <%= button_to('Destroy', 'http://www.example.com',
-
# method: "delete", remote: true, data: { confirm: 'Are you sure?', disable_with: 'loading...' }) %>
-
# # => "<form class='button_to' method='post' action='http://www.example.com' data-remote='true'>
-
# # <div>
-
# # <input name='_method' value='delete' type='hidden' />
-
# # <input value='Destroy' type='submit' data-disable-with='loading...' data-confirm='Are you sure?' />
-
# # <input name="authenticity_token" type="hidden" value="10f2163b45388899ad4d5ae948988266befcb6c3d1b2451cf657a0c293d605a6"/>
-
# # </div>
-
# # </form>"
-
# #
-
1
def button_to(name = nil, options = nil, html_options = nil, &block)
-
html_options, options = options, name if block_given?
-
options ||= {}
-
html_options ||= {}
-
-
html_options = html_options.stringify_keys
-
convert_boolean_attributes!(html_options, %w(disabled))
-
-
url = options.is_a?(String) ? options : url_for(options)
-
remote = html_options.delete('remote')
-
-
method = html_options.delete('method').to_s
-
method_tag = %w{patch put delete}.include?(method) ? method_tag(method) : ''.html_safe
-
-
form_method = method == 'get' ? 'get' : 'post'
-
form_options = html_options.delete('form') || {}
-
form_options[:class] ||= html_options.delete('form_class') || 'button_to'
-
form_options.merge!(method: form_method, action: url)
-
form_options.merge!("data-remote" => "true") if remote
-
-
request_token_tag = form_method == 'post' ? token_tag : ''
-
-
html_options = convert_options_to_data_attributes(options, html_options)
-
html_options['type'] = 'submit'
-
-
button = if block_given?
-
content_tag('button', html_options, &block)
-
else
-
html_options['value'] = name || url
-
tag('input', html_options)
-
end
-
-
inner_tags = method_tag.safe_concat(button).safe_concat(request_token_tag)
-
content_tag('form', content_tag('div', inner_tags), form_options)
-
end
-
-
# Creates a link tag of the given +name+ using a URL created by the set of
-
# +options+ unless the current request URI is the same as the links, in
-
# which case only the name is returned (or the given block is yielded, if
-
# one exists). You can give +link_to_unless_current+ a block which will
-
# specialize the default behavior (e.g., show a "Start Here" link rather
-
# than the link's text).
-
#
-
# ==== Examples
-
# Let's say you have a navigation menu...
-
#
-
# <ul id="navbar">
-
# <li><%= link_to_unless_current("Home", { action: "index" }) %></li>
-
# <li><%= link_to_unless_current("About Us", { action: "about" }) %></li>
-
# </ul>
-
#
-
# If in the "about" action, it will render...
-
#
-
# <ul id="navbar">
-
# <li><a href="/controller/index">Home</a></li>
-
# <li>About Us</li>
-
# </ul>
-
#
-
# ...but if in the "index" action, it will render:
-
#
-
# <ul id="navbar">
-
# <li>Home</li>
-
# <li><a href="/controller/about">About Us</a></li>
-
# </ul>
-
#
-
# The implicit block given to +link_to_unless_current+ is evaluated if the current
-
# action is the action given. So, if we had a comments page and wanted to render a
-
# "Go Back" link instead of a link to the comments page, we could do something like this...
-
#
-
# <%=
-
# link_to_unless_current("Comment", { controller: "comments", action: "new" }) do
-
# link_to("Go back", { controller: "posts", action: "index" })
-
# end
-
# %>
-
1
def link_to_unless_current(name, options = {}, html_options = {}, &block)
-
link_to_unless current_page?(options), name, options, html_options, &block
-
end
-
-
# Creates a link tag of the given +name+ using a URL created by the set of
-
# +options+ unless +condition+ is true, in which case only the name is
-
# returned. To specialize the default behavior (i.e., show a login link rather
-
# than just the plaintext link text), you can pass a block that
-
# accepts the name or the full argument list for +link_to_unless+.
-
#
-
# ==== Examples
-
# <%= link_to_unless(@current_user.nil?, "Reply", { action: "reply" }) %>
-
# # If the user is logged in...
-
# # => <a href="/controller/reply/">Reply</a>
-
#
-
# <%=
-
# link_to_unless(@current_user.nil?, "Reply", { action: "reply" }) do |name|
-
# link_to(name, { controller: "accounts", action: "signup" })
-
# end
-
# %>
-
# # If the user is logged in...
-
# # => <a href="/controller/reply/">Reply</a>
-
# # If not...
-
# # => <a href="/accounts/signup">Reply</a>
-
1
def link_to_unless(condition, name, options = {}, html_options = {}, &block)
-
if condition
-
if block_given?
-
block.arity <= 1 ? capture(name, &block) : capture(name, options, html_options, &block)
-
else
-
name
-
end
-
else
-
link_to(name, options, html_options)
-
end
-
end
-
-
# Creates a link tag of the given +name+ using a URL created by the set of
-
# +options+ if +condition+ is true, otherwise only the name is
-
# returned. To specialize the default behavior, you can pass a block that
-
# accepts the name or the full argument list for +link_to_unless+ (see the examples
-
# in +link_to_unless+).
-
#
-
# ==== Examples
-
# <%= link_to_if(@current_user.nil?, "Login", { controller: "sessions", action: "new" }) %>
-
# # If the user isn't logged in...
-
# # => <a href="/sessions/new/">Login</a>
-
#
-
# <%=
-
# link_to_if(@current_user.nil?, "Login", { controller: "sessions", action: "new" }) do
-
# link_to(@current_user.login, { controller: "accounts", action: "show", id: @current_user })
-
# end
-
# %>
-
# # If the user isn't logged in...
-
# # => <a href="/sessions/new/">Login</a>
-
# # If they are logged in...
-
# # => <a href="/accounts/show/3">my_username</a>
-
1
def link_to_if(condition, name, options = {}, html_options = {}, &block)
-
link_to_unless !condition, name, options, html_options, &block
-
end
-
-
# Creates a mailto link tag to the specified +email_address+, which is
-
# also used as the name of the link unless +name+ is specified. Additional
-
# HTML attributes for the link can be passed in +html_options+.
-
#
-
# +mail_to+ has several methods for hindering email harvesters and customizing
-
# the email itself by passing special keys to +html_options+.
-
#
-
# ==== Options
-
# * <tt>:encode</tt> - This key will accept the strings "javascript" or "hex".
-
# Passing "javascript" will dynamically create and encode the mailto link then
-
# eval it into the DOM of the page. This method will not show the link on
-
# the page if the user has JavaScript disabled. Passing "hex" will hex
-
# encode the +email_address+ before outputting the mailto link.
-
# * <tt>:replace_at</tt> - When the link +name+ isn't provided, the
-
# +email_address+ is used for the link label. You can use this option to
-
# obfuscate the +email_address+ by substituting the @ sign with the string
-
# given as the value.
-
# * <tt>:replace_dot</tt> - When the link +name+ isn't provided, the
-
# +email_address+ is used for the link label. You can use this option to
-
# obfuscate the +email_address+ by substituting the . in the email with the
-
# string given as the value.
-
# * <tt>:subject</tt> - Preset the subject line of the email.
-
# * <tt>:body</tt> - Preset the body of the email.
-
# * <tt>:cc</tt> - Carbon Copy additional recipients on the email.
-
# * <tt>:bcc</tt> - Blind Carbon Copy additional recipients on the email.
-
#
-
# ==== Examples
-
# mail_to "me@domain.com"
-
# # => <a href="mailto:me@domain.com">me@domain.com</a>
-
#
-
# mail_to "me@domain.com", "My email", encode: "javascript"
-
# # => <script>eval(decodeURIComponent('%64%6f%63...%27%29%3b'))</script>
-
#
-
# mail_to "me@domain.com", "My email", encode: "hex"
-
# # => <a href="mailto:%6d%65@%64%6f%6d%61%69%6e.%63%6f%6d">My email</a>
-
#
-
# mail_to "me@domain.com", nil, replace_at: "_at_", replace_dot: "_dot_", class: "email"
-
# # => <a href="mailto:me@domain.com" class="email">me_at_domain_dot_com</a>
-
#
-
# mail_to "me@domain.com", "My email", cc: "ccaddress@domain.com",
-
# subject: "This is an example email"
-
# # => <a href="mailto:me@domain.com?cc=ccaddress@domain.com&subject=This%20is%20an%20example%20email">My email</a>
-
1
def mail_to(email_address, name = nil, html_options = {})
-
email_address = ERB::Util.html_escape(email_address)
-
-
html_options = html_options.stringify_keys
-
encode = html_options.delete("encode").to_s
-
-
extras = %w{ cc bcc body subject }.map { |item|
-
option = html_options.delete(item) || next
-
"#{item}=#{Rack::Utils.escape_path(option)}"
-
}.compact
-
extras = extras.empty? ? '' : '?' + ERB::Util.html_escape(extras.join('&'))
-
-
email_address_obfuscated = email_address.to_str
-
email_address_obfuscated.gsub!(/@/, html_options.delete("replace_at")) if html_options.key?("replace_at")
-
email_address_obfuscated.gsub!(/\./, html_options.delete("replace_dot")) if html_options.key?("replace_dot")
-
case encode
-
when "javascript"
-
string = ''
-
html = content_tag("a", name || email_address_obfuscated.html_safe, html_options.merge("href" => "mailto:#{email_address}#{extras}".html_safe))
-
html = escape_javascript(html.to_str)
-
"document.write('#{html}');".each_byte do |c|
-
string << sprintf("%%%x", c)
-
end
-
"<script>eval(decodeURIComponent('#{string}'))</script>".html_safe
-
when "hex"
-
email_address_encoded = email_address_obfuscated.unpack('C*').map {|c|
-
sprintf("&#%d;", c)
-
}.join
-
-
string = 'mailto:'.unpack('C*').map { |c|
-
sprintf("&#%d;", c)
-
}.join + email_address.unpack('C*').map { |c|
-
char = c.chr
-
char =~ /\w/ ? sprintf("%%%x", c) : char
-
}.join
-
-
content_tag "a", name || email_address_encoded.html_safe, html_options.merge("href" => "#{string}#{extras}".html_safe)
-
else
-
content_tag "a", name || email_address_obfuscated.html_safe, html_options.merge("href" => "mailto:#{email_address}#{extras}".html_safe)
-
end
-
end
-
-
# True if the current request URI was generated by the given +options+.
-
#
-
# ==== Examples
-
# Let's say we're in the <tt>/shop/checkout?order=desc</tt> action.
-
#
-
# current_page?(action: 'process')
-
# # => false
-
#
-
# current_page?(controller: 'shop', action: 'checkout')
-
# # => true
-
#
-
# current_page?(controller: 'shop', action: 'checkout', order: 'asc')
-
# # => false
-
#
-
# current_page?(action: 'checkout')
-
# # => true
-
#
-
# current_page?(controller: 'library', action: 'checkout')
-
# # => false
-
#
-
# Let's say we're in the <tt>/shop/checkout?order=desc&page=1</tt> action.
-
#
-
# current_page?(action: 'process')
-
# # => false
-
#
-
# current_page?(controller: 'shop', action: 'checkout')
-
# # => true
-
#
-
# current_page?(controller: 'shop', action: 'checkout', order: 'desc', page: '1')
-
# # => true
-
#
-
# current_page?(controller: 'shop', action: 'checkout', order: 'desc', page: '2')
-
# # => false
-
#
-
# current_page?(controller: 'shop', action: 'checkout', order: 'desc')
-
# # => false
-
#
-
# current_page?(action: 'checkout')
-
# # => true
-
#
-
# current_page?(controller: 'library', action: 'checkout')
-
# # => false
-
#
-
# Let's say we're in the <tt>/products</tt> action with method POST in case of invalid product.
-
#
-
# current_page?(controller: 'product', action: 'index')
-
# # => false
-
#
-
1
def current_page?(options)
-
unless request
-
raise "You cannot use helpers that need to determine the current " \
-
"page unless your view context provides a Request object " \
-
"in a #request method"
-
end
-
-
return false unless request.get?
-
-
url_string = url_for(options)
-
-
# We ignore any extra parameters in the request_uri if the
-
# submitted url doesn't have any either. This lets the function
-
# work with things like ?order=asc
-
request_uri = url_string.index("?") ? request.fullpath : request.path
-
-
if url_string =~ /^\w+:\/\//
-
url_string == "#{request.protocol}#{request.host_with_port}#{request_uri}"
-
else
-
url_string == request_uri
-
end
-
end
-
-
1
private
-
1
def convert_options_to_data_attributes(options, html_options)
-
if html_options
-
html_options = html_options.stringify_keys
-
html_options['data-remote'] = 'true' if link_to_remote_options?(options) || link_to_remote_options?(html_options)
-
-
disable_with = html_options.delete("disable_with")
-
confirm = html_options.delete('confirm')
-
method = html_options.delete('method')
-
-
if confirm
-
message = ":confirm option is deprecated and will be removed from Rails 4.1. " \
-
"Use 'data: { confirm: \'Text\' }' instead."
-
ActiveSupport::Deprecation.warn message
-
-
html_options["data-confirm"] = confirm
-
end
-
-
add_method_to_attributes!(html_options, method) if method
-
-
if disable_with
-
message = ":disable_with option is deprecated and will be removed from Rails 4.1. " \
-
"Use 'data: { disable_with: \'Text\' }' instead."
-
ActiveSupport::Deprecation.warn message
-
-
html_options["data-disable-with"] = disable_with
-
end
-
-
html_options
-
else
-
link_to_remote_options?(options) ? {'data-remote' => 'true'} : {}
-
end
-
end
-
-
1
def link_to_remote_options?(options)
-
if options.is_a?(Hash)
-
options.delete('remote') || options.delete(:remote)
-
end
-
end
-
-
1
def add_method_to_attributes!(html_options, method)
-
if method && method.to_s.downcase != "get" && html_options["rel"] !~ /nofollow/
-
html_options["rel"] = "#{html_options["rel"]} nofollow".lstrip
-
end
-
html_options["data-method"] = method
-
end
-
-
# Processes the +html_options+ hash, converting the boolean
-
# attributes from true/false form into the form required by
-
# HTML/XHTML. (An attribute is considered to be boolean if
-
# its name is listed in the given +bool_attrs+ array.)
-
#
-
# More specifically, for each boolean attribute in +html_options+
-
# given as:
-
#
-
# "attr" => bool_value
-
#
-
# if the associated +bool_value+ evaluates to true, it is
-
# replaced with the attribute's name; otherwise the attribute is
-
# removed from the +html_options+ hash. (See the XHTML 1.0 spec,
-
# section 4.5 "Attribute Minimization" for more:
-
# http://www.w3.org/TR/xhtml1/#h-4.5)
-
#
-
# Returns the updated +html_options+ hash, which is also modified
-
# in place.
-
#
-
# Example:
-
#
-
# convert_boolean_attributes!( html_options,
-
# %w( checked disabled readonly ) )
-
1
def convert_boolean_attributes!(html_options, bool_attrs)
-
bool_attrs.each { |x| html_options[x] = x if html_options.delete(x) }
-
html_options
-
end
-
-
1
def token_tag(token=nil)
-
if token != false && protect_against_forgery?
-
token ||= form_authenticity_token
-
tag(:input, type: "hidden", name: request_forgery_protection_token.to_s, value: token)
-
else
-
''
-
end
-
end
-
-
1
def method_tag(method)
-
tag('input', type: 'hidden', name: '_method', value: method.to_s)
-
end
-
end
-
end
-
end
-
1
module ActionView
-
# = Action View Log Subscriber
-
#
-
# Provides functionality so that Rails can output logs from Action View.
-
1
class LogSubscriber < ActiveSupport::LogSubscriber
-
1
VIEWS_PATTERN = /^app\/views\//.freeze
-
-
1
def render_template(event)
-
return unless logger.info?
-
message = " Rendered #{from_rails_root(event.payload[:identifier])}"
-
message << " within #{from_rails_root(event.payload[:layout])}" if event.payload[:layout]
-
message << " (#{event.duration.round(1)}ms)"
-
info(message)
-
end
-
1
alias :render_partial :render_template
-
1
alias :render_collection :render_template
-
-
1
def logger
-
4
ActionView::Base.logger
-
end
-
-
1
protected
-
-
1
def from_rails_root(string)
-
string.sub("#{Rails.root}/", "").sub(VIEWS_PATTERN, "")
-
end
-
end
-
end
-
-
1
ActionView::LogSubscriber.attach_to :action_view
-
1
require 'active_support/core_ext/module/remove_method'
-
-
1
module ActionView
-
# = Action View Lookup Context
-
#
-
# LookupContext is the object responsible to hold all information required to lookup
-
# templates, i.e. view paths and details. The LookupContext is also responsible to
-
# generate a key, given to view paths, used in the resolver cache lookup. Since
-
# this key is generated just once during the request, it speeds up all cache accesses.
-
1
class LookupContext #:nodoc:
-
1
attr_accessor :prefixes, :rendered_format
-
-
1
mattr_accessor :fallbacks
-
1
@@fallbacks = FallbackFileSystemResolver.instances
-
-
1
mattr_accessor :registered_details
-
1
self.registered_details = []
-
-
1
def self.register_detail(name, options = {}, &block)
-
3
self.registered_details << name
-
9
initialize = registered_details.map { |n| "@details[:#{n}] = details[:#{n}] || default_#{n}" }
-
-
3
Accessors.send :define_method, :"default_#{name}", &block
-
3
Accessors.module_eval <<-METHOD, __FILE__, __LINE__ + 1
-
def #{name}
-
@details.fetch(:#{name}, [])
-
end
-
-
def #{name}=(value)
-
value = value.present? ? Array(value) : default_#{name}
-
_set_detail(:#{name}, value) if value != @details[:#{name}]
-
end
-
-
remove_possible_method :initialize_details
-
def initialize_details(details)
-
#{initialize.join("\n")}
-
end
-
METHOD
-
end
-
-
# Holds accessors for the registered details.
-
1
module Accessors #:nodoc:
-
end
-
-
122
register_detail(:locale) { [I18n.locale, I18n.default_locale].uniq }
-
122
register_detail(:formats) { ActionView::Base.default_formats || [:html, :text, :js, :css, :xml, :json] }
-
122
register_detail(:handlers){ Template::Handlers.extensions }
-
-
1
class DetailsKey #:nodoc:
-
1
alias :eql? :equal?
-
1
alias :object_hash :hash
-
-
1
attr_reader :hash
-
1
@details_keys = Hash.new
-
-
1
def self.get(details)
-
188
@details_keys[details] ||= new
-
end
-
-
1
def self.clear
-
@details_keys.clear
-
end
-
-
1
def initialize
-
8
@hash = object_hash
-
end
-
end
-
-
# Add caching behavior on top of Details.
-
1
module DetailsCache
-
1
attr_accessor :cache
-
-
# Calculate the details key. Remove the handlers from calculation to improve performance
-
# since the user cannot modify it explicitly.
-
1
def details_key #:nodoc:
-
298
@details_key ||= DetailsKey.get(@details) if @cache
-
end
-
-
# Temporary skip passing the details_key forward.
-
1
def disable_cache
-
old_value, @cache = @cache, false
-
yield
-
ensure
-
@cache = old_value
-
end
-
-
1
protected
-
-
1
def _set_detail(key, value)
-
168
@details = @details.dup if @details_key
-
168
@details_key = nil
-
168
@details[key] = value
-
end
-
end
-
-
# Helpers related to template lookup using the lookup context information.
-
1
module ViewPaths
-
1
attr_reader :view_paths, :html_fallback_for_js
-
-
# Whenever setting view paths, makes a copy so we can manipulate then in
-
# instance objects as we wish.
-
1
def view_paths=(paths)
-
121
@view_paths = ActionView::PathSet.new(Array(paths))
-
end
-
-
1
def find(name, prefixes = [], partial = false, keys = [], options = {})
-
20
@view_paths.find(*args_for_lookup(name, prefixes, partial, keys, options))
-
end
-
1
alias :find_template :find
-
-
1
def find_all(name, prefixes = [], partial = false, keys = [], options = {})
-
279
@view_paths.find_all(*args_for_lookup(name, prefixes, partial, keys, options))
-
end
-
-
1
def exists?(name, prefixes = [], partial = false, keys = [], options = {})
-
@view_paths.exists?(*args_for_lookup(name, prefixes, partial, keys, options))
-
end
-
1
alias :template_exists? :exists?
-
-
# Add fallbacks to the view paths. Useful in cases you are rendering a :file.
-
1
def with_fallbacks
-
added_resolvers = 0
-
self.class.fallbacks.each do |resolver|
-
next if view_paths.include?(resolver)
-
view_paths.push(resolver)
-
added_resolvers += 1
-
end
-
yield
-
ensure
-
added_resolvers.times { view_paths.pop }
-
end
-
-
1
protected
-
-
1
def args_for_lookup(name, prefixes, partial, keys, details_options) #:nodoc:
-
299
name, prefixes = normalize_name(name, prefixes)
-
299
details, details_key = detail_args_for(details_options)
-
299
[name, prefixes, partial || false, details, details_key, keys]
-
end
-
-
# Compute details hash and key according to user options (e.g. passed from #render).
-
1
def detail_args_for(options)
-
299
return @details, details_key if options.empty? # most common path.
-
1
user_details = @details.merge(options)
-
1
[user_details, DetailsKey.get(user_details)]
-
end
-
-
# Support legacy foo.erb names even though we now ignore .erb
-
# as well as incorrectly putting part of the path in the template
-
# name instead of the prefix.
-
1
def normalize_name(name, prefixes) #:nodoc:
-
299
prefixes = prefixes.presence
-
299
parts = name.to_s.split('/')
-
299
parts.shift if parts.first.empty?
-
299
name = parts.pop
-
-
299
return name, prefixes || [""] if parts.empty?
-
-
119
parts = parts.join('/')
-
232
prefixes = prefixes ? prefixes.map { |p| "#{p}/#{parts}" } : [parts]
-
-
119
return name, prefixes
-
end
-
end
-
-
1
include Accessors
-
1
include DetailsCache
-
1
include ViewPaths
-
-
1
def initialize(view_paths, details = {}, prefixes = [])
-
121
@details, @details_key = {}, nil
-
121
@skip_default_locale = false
-
121
@cache = true
-
121
@prefixes = prefixes
-
121
@rendered_format = nil
-
-
121
self.view_paths = view_paths
-
121
initialize_details(details)
-
end
-
-
# Override formats= to expand ["*/*"] values and automatically
-
# add :html as fallback to :js.
-
1
def formats=(values)
-
241
if values
-
241
values.concat(default_formats) if values.delete "*/*"
-
241
if values == [:js]
-
values << :html
-
@html_fallback_for_js = true
-
end
-
end
-
241
super(values)
-
end
-
-
# Do not use the default locale on template lookup.
-
1
def skip_default_locale!
-
120
@skip_default_locale = true
-
120
self.locale = nil
-
end
-
-
# Override locale to return a symbol instead of array.
-
1
def locale
-
@details[:locale].first
-
end
-
-
# Overload locale= to also set the I18n.locale. If the current I18n.config object responds
-
# to original_config, it means that it's has a copy of the original I18n configuration and it's
-
# acting as proxy, which we need to skip.
-
1
def locale=(value)
-
121
if value
-
1
config = I18n.config.respond_to?(:original_config) ? I18n.config.original_config : I18n.config
-
1
config.locale = value
-
end
-
-
121
super(@skip_default_locale ? I18n.locale : default_locale)
-
end
-
-
# A method which only uses the first format in the formats array for layout lookup.
-
1
def with_layout_format
-
106
if formats.size == 1
-
65
yield
-
else
-
41
old_formats = formats
-
41
_set_detail(:formats, formats[0,1])
-
-
41
begin
-
41
yield
-
ensure
-
41
_set_detail(:formats, old_formats)
-
end
-
end
-
end
-
end
-
end
-
1
module ActionView
-
1
module ModelNaming
-
# Converts the given object to an ActiveModel compliant one.
-
1
def convert_to_model(object)
-
object.respond_to?(:to_model) ? object.to_model : object
-
end
-
-
1
def model_name_from_record_or_class(record_or_class)
-
(record_or_class.is_a?(Class) ? record_or_class : convert_to_model(record_or_class).class).model_name
-
end
-
end
-
end
-
1
module ActionView #:nodoc:
-
# = Action View PathSet
-
1
class PathSet #:nodoc:
-
1
include Enumerable
-
-
1
attr_reader :paths
-
-
1
delegate :[], :include?, :pop, :size, :each, to: :paths
-
-
1
def initialize(paths = [])
-
128
@paths = typecast paths
-
end
-
-
1
def initialize_copy(other)
-
2
@paths = other.paths.dup
-
2
self
-
end
-
-
1
def to_ary
-
125
paths.dup
-
end
-
-
1
def compact
-
PathSet.new paths.compact
-
end
-
-
1
def +(array)
-
PathSet.new(paths + array)
-
end
-
-
1
%w(<< concat push insert unshift).each do |method|
-
5
class_eval <<-METHOD, __FILE__, __LINE__ + 1
-
def #{method}(*args)
-
paths.#{method}(*typecast(args))
-
end
-
METHOD
-
end
-
-
1
def find(*args)
-
20
find_all(*args).first || raise(MissingTemplate.new(self, *args))
-
end
-
-
1
def find_all(path, prefixes = [], *args)
-
299
prefixes = [prefixes] if String === prefixes
-
299
prefixes.each do |prefix|
-
300
paths.each do |resolver|
-
305
templates = resolver.find_all(path, prefix, *args)
-
305
return templates unless templates.empty?
-
end
-
end
-
194
[]
-
end
-
-
1
def exists?(path, prefixes, *args)
-
find_all(path, prefixes, *args).any?
-
end
-
-
1
private
-
-
1
def typecast(paths)
-
128
paths.map do |path|
-
129
case path
-
when Pathname, String
-
3
OptimizedFileSystemResolver.new path.to_s
-
else
-
126
path
-
end
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/module'
-
1
require 'action_view/model_naming'
-
-
1
module ActionView
-
# The record identifier encapsulates a number of naming conventions for dealing with records, like Active Records or
-
# pretty much any other model type that has an id. These patterns are then used to try elevate the view actions to
-
# a higher logical level.
-
#
-
# # routes
-
# resources :posts
-
#
-
# # view
-
# <%= div_for(post) do %> <div id="post_45" class="post">
-
# <%= post.body %> What a wonderful world!
-
# <% end %> </div>
-
#
-
# # controller
-
# def update
-
# post = Post.find(params[:id])
-
# post.update_attributes(params[:post])
-
#
-
# redirect_to(post) # Calls polymorphic_url(post) which in turn calls post_url(post)
-
# end
-
#
-
# As the example above shows, you can stop caring to a large extent what the actual id of the post is.
-
# You just know that one is being assigned and that the subsequent calls in redirect_to expect that
-
# same naming convention and allows you to write less code if you follow it.
-
1
module RecordIdentifier
-
1
extend self
-
1
extend ModelNaming
-
-
1
include ModelNaming
-
-
1
JOIN = '_'.freeze
-
1
NEW = 'new'.freeze
-
-
# The DOM class convention is to use the singular form of an object or class.
-
#
-
# dom_class(post) # => "post"
-
# dom_class(Person) # => "person"
-
#
-
# If you need to address multiple instances of the same class in the same view, you can prefix the dom_class:
-
#
-
# dom_class(post, :edit) # => "edit_post"
-
# dom_class(Person, :edit) # => "edit_person"
-
1
def dom_class(record_or_class, prefix = nil)
-
singular = model_name_from_record_or_class(record_or_class).param_key
-
prefix ? "#{prefix}#{JOIN}#{singular}" : singular
-
end
-
-
# The DOM id convention is to use the singular form of an object or class with the id following an underscore.
-
# If no id is found, prefix with "new_" instead.
-
#
-
# dom_id(Post.find(45)) # => "post_45"
-
# dom_id(Post.new) # => "new_post"
-
#
-
# If you need to address multiple instances of the same class in the same view, you can prefix the dom_id:
-
#
-
# dom_id(Post.find(45), :edit) # => "edit_post_45"
-
# dom_id(Post.new, :custom) # => "custom_post"
-
1
def dom_id(record, prefix = nil)
-
if record_id = record_key_for_dom_id(record)
-
"#{dom_class(record, prefix)}#{JOIN}#{record_id}"
-
else
-
dom_class(record, prefix || NEW)
-
end
-
end
-
-
1
protected
-
-
# Returns a string representation of the key attribute(s) that is suitable for use in an HTML DOM id.
-
# This can be overwritten to customize the default generated string representation if desired.
-
# If you need to read back a key from a dom_id in order to query for the underlying database record,
-
# you should write a helper like 'person_record_from_dom_id' that will extract the key either based
-
# on the default implementation (which just joins all key attributes with '_') or on your own
-
# overwritten version of the method. By default, this implementation passes the key string through a
-
# method that replaces all characters that are invalid inside DOM ids, with valid ones. You need to
-
# make sure yourself that your dom ids are valid, in case you overwrite this method.
-
1
def record_key_for_dom_id(record)
-
key = convert_to_model(record).to_key
-
key ? key.join('_') : key
-
end
-
end
-
end
-
1
module ActionView
-
1
class AbstractRenderer #:nodoc:
-
1
delegate :find_template, :template_exists?, :with_fallbacks, :with_layout_format, :formats, :to => :@lookup_context
-
-
1
def initialize(lookup_context)
-
115
@lookup_context = lookup_context
-
end
-
-
1
def render
-
raise NotImplementedError
-
end
-
-
1
protected
-
-
1
def extract_details(options)
-
137
@lookup_context.registered_details.each_with_object({}) do |key, details|
-
411
next unless value = options[key]
-
1
details[key] = Array(value)
-
end
-
end
-
-
1
def instrument(name, options={})
-
274
ActiveSupport::Notifications.instrument("render_#{name}.action_view", options){ yield }
-
end
-
-
1
def prepend_formats(formats)
-
137
formats = Array(formats)
-
137
return if formats.empty? || @lookup_context.html_fallback_for_js
-
118
@lookup_context.formats = formats | @lookup_context.formats
-
end
-
end
-
end
-
1
module ActionView
-
# This is the main entry point for rendering. It basically delegates
-
# to other objects like TemplateRenderer and PartialRenderer which
-
# actually renders the template.
-
1
class Renderer
-
1
attr_accessor :lookup_context
-
-
1
def initialize(lookup_context)
-
115
@lookup_context = lookup_context
-
end
-
-
# Main render entry point shared by AV and AC.
-
1
def render(context, options)
-
137
if options.key?(:partial)
-
render_partial(context, options)
-
else
-
137
render_template(context, options)
-
end
-
end
-
-
# Render but returns a valid Rack body. If fibers are defined, we return
-
# a streaming body that renders the template piece by piece.
-
#
-
# Note that partials are not supported to be rendered with streaming,
-
# so in such cases, we just wrap them in an array.
-
1
def render_body(context, options)
-
if options.key?(:partial)
-
[render_partial(context, options)]
-
else
-
StreamingTemplateRenderer.new(@lookup_context).render(context, options)
-
end
-
end
-
-
# Direct accessor to template rendering.
-
1
def render_template(context, options) #:nodoc:
-
137
_template_renderer.render(context, options)
-
end
-
-
# Direct access to partial rendering.
-
1
def render_partial(context, options, &block) #:nodoc:
-
_partial_renderer.render(context, options, block)
-
end
-
-
1
private
-
-
1
def _template_renderer #:nodoc:
-
137
@_template_renderer ||= TemplateRenderer.new(@lookup_context)
-
end
-
-
1
def _partial_renderer #:nodoc:
-
@_partial_renderer ||= PartialRenderer.new(@lookup_context)
-
end
-
end
-
end
-
1
require 'active_support/core_ext/object/try'
-
-
1
module ActionView
-
1
class TemplateRenderer < AbstractRenderer #:nodoc:
-
1
def render(context, options)
-
137
@view = context
-
137
@details = extract_details(options)
-
137
template = determine_template(options)
-
137
context = @lookup_context
-
-
137
prepend_formats(template.formats)
-
-
137
unless context.rendered_format
-
115
context.rendered_format = template.formats.first || formats.last
-
end
-
-
137
render_template(template, options[:layout], options[:locals])
-
end
-
-
# Determine the template to be rendered using the given options.
-
1
def determine_template(options) #:nodoc:
-
137
keys = options.fetch(:locals, {}).keys
-
-
137
if options.key?(:text)
-
13
Template::Text.new(options[:text], formats.first)
-
124
elsif options.key?(:file)
-
with_fallbacks { find_template(options[:file], nil, false, keys, @details) }
-
124
elsif options.key?(:inline)
-
19
handler = Template.handler_for_extension(options[:type] || "erb")
-
19
Template.new(options[:inline], "inline template", handler, :locals => keys)
-
105
elsif options.key?(:template)
-
105
if options[:template].respond_to?(:render)
-
89
options[:template]
-
else
-
16
find_template(options[:template], options[:prefixes], false, keys, @details)
-
end
-
else
-
raise ArgumentError, "You invoked render but did not give any of :partial, :template, :inline, :file or :text option."
-
end
-
end
-
-
# Renders the given template. A string representing the layout can be
-
# supplied as well.
-
1
def render_template(template, layout_name = nil, locals = {}) #:nodoc:
-
137
view, locals = @view, locals || {}
-
-
137
render_with_layout(layout_name, locals) do |layout|
-
137
instrument(:template, :identifier => template.identifier, :layout => layout.try(:virtual_path)) do
-
137
template.render(view, locals) { |*name| view._layout_for(*name) }
-
end
-
end
-
end
-
-
1
def render_with_layout(path, locals) #:nodoc:
-
137
layout = path && find_layout(path, locals.keys)
-
137
content = yield(layout)
-
-
137
if layout
-
9
view = @view
-
9
view.view_flow.set(:layout, content)
-
18
layout.render(view, locals){ |*name| view._layout_for(*name) }
-
else
-
128
content
-
end
-
end
-
-
# This is the method which actually finds the layout using details in the lookup
-
# context object. If no layout is found, it checks if at least a layout with
-
# the given name exists across all details before raising the error.
-
1
def find_layout(layout, keys)
-
212
with_layout_format { resolve_layout(layout, keys) }
-
end
-
-
1
def resolve_layout(layout, keys)
-
209
case layout
-
when String
-
4
begin
-
4
if layout =~ /^\//
-
with_fallbacks { find_template(layout, nil, false, keys, @details) }
-
else
-
4
find_template(layout, nil, false, keys, @details)
-
end
-
rescue ActionView::MissingTemplate
-
all_details = @details.merge(:formats => @lookup_context.default_formats)
-
raise unless template_exists?(layout, nil, false, keys, all_details)
-
end
-
when Proc
-
103
resolve_layout(layout.call, keys)
-
when FalseClass
-
nil
-
else
-
102
layout
-
end
-
end
-
end
-
end
-
1
module ActionView
-
1
module RoutingUrlFor
-
-
# Returns the URL for the set of +options+ provided. This takes the
-
# same options as +url_for+ in Action Controller (see the
-
# documentation for <tt>ActionController::Base#url_for</tt>). Note that by default
-
# <tt>:only_path</tt> is <tt>true</tt> so you'll get the relative "/controller/action"
-
# instead of the fully qualified URL like "http://example.com/controller/action".
-
#
-
# ==== Options
-
# * <tt>:anchor</tt> - Specifies the anchor name to be appended to the path.
-
# * <tt>:only_path</tt> - If true, returns the relative URL (omitting the protocol, host name, and port) (<tt>true</tt> by default unless <tt>:host</tt> is specified).
-
# * <tt>:trailing_slash</tt> - If true, adds a trailing slash, as in "/archive/2005/". Note that this
-
# is currently not recommended since it breaks caching.
-
# * <tt>:host</tt> - Overrides the default (current) host if provided.
-
# * <tt>:protocol</tt> - Overrides the default (current) protocol if provided.
-
# * <tt>:user</tt> - Inline HTTP authentication (only plucked out if <tt>:password</tt> is also present).
-
# * <tt>:password</tt> - Inline HTTP authentication (only plucked out if <tt>:user</tt> is also present).
-
#
-
# ==== Relying on named routes
-
#
-
# Passing a record (like an Active Record) instead of a hash as the options parameter will
-
# trigger the named route for that record. The lookup will happen on the name of the class. So passing a
-
# Workshop object will attempt to use the +workshop_path+ route. If you have a nested route, such as
-
# +admin_workshop_path+ you'll have to call that explicitly (it's impossible for +url_for+ to guess that route).
-
#
-
# ==== Implicit Controller Namespacing
-
#
-
# Controllers passed in using the +:controller+ option will retain their namespace unless it is an absolute one.
-
#
-
# ==== Examples
-
# <%= url_for(action: 'index') %>
-
# # => /blog/
-
#
-
# <%= url_for(action: 'find', controller: 'books') %>
-
# # => /books/find
-
#
-
# <%= url_for(action: 'login', controller: 'members', only_path: false, protocol: 'https') %>
-
# # => https://www.example.com/members/login/
-
#
-
# <%= url_for(action: 'play', anchor: 'player') %>
-
# # => /messages/play/#player
-
#
-
# <%= url_for(action: 'jump', anchor: 'tax&ship') %>
-
# # => /testing/jump/#tax&ship
-
#
-
# <%= url_for(Workshop.new) %>
-
# # relies on Workshop answering a persisted? call (and in this case returning false)
-
# # => /workshops
-
#
-
# <%= url_for(@workshop) %>
-
# # calls @workshop.to_param which by default returns the id
-
# # => /workshops/5
-
#
-
# # to_param can be re-defined in a model to provide different URL names:
-
# # => /workshops/1-workshop-name
-
#
-
# <%= url_for("http://www.example.com") %>
-
# # => http://www.example.com
-
#
-
# <%= url_for(:back) %>
-
# # if request.env["HTTP_REFERER"] is set to "http://www.example.com"
-
# # => http://www.example.com
-
#
-
# <%= url_for(:back) %>
-
# # if request.env["HTTP_REFERER"] is not set or is blank
-
# # => javascript:history.back()
-
#
-
# <%= url_for(action: 'index', controller: 'users') %>
-
# # Assuming an "admin" namespace
-
# # => /admin/users
-
#
-
# <%= url_for(action: 'index', controller: '/users') %>
-
# # Specify absolute path with beginning slash
-
# # => /users
-
1
def url_for(options = nil)
-
2
case options
-
when String
-
options
-
when nil, Hash
-
2
options ||= {}
-
2
options = { :only_path => options[:host].nil? }.merge!(options.symbolize_keys)
-
2
super
-
when :back
-
_back_url
-
else
-
polymorphic_path(options)
-
end
-
end
-
-
1
def url_options #:nodoc:
-
2
return super unless controller.respond_to?(:url_options)
-
2
controller.url_options
-
end
-
-
1
def _routes_context #:nodoc:
-
controller
-
end
-
1
protected :_routes_context
-
-
1
def optimize_routes_generation? #:nodoc:
-
2
controller.respond_to?(:optimize_routes_generation?, true) ?
-
controller.optimize_routes_generation? : super
-
end
-
1
protected :optimize_routes_generation?
-
end
-
end
-
1
require 'active_support/core_ext/object/try'
-
1
require 'active_support/core_ext/kernel/singleton_class'
-
1
require 'active_support/deprecation'
-
1
require 'thread'
-
-
1
module ActionView
-
# = Action View Template
-
1
class Template
-
1
extend ActiveSupport::Autoload
-
-
# === Encodings in ActionView::Template
-
#
-
# ActionView::Template is one of a few sources of potential
-
# encoding issues in Rails. This is because the source for
-
# templates are usually read from disk, and Ruby (like most
-
# encoding-aware programming languages) assumes that the
-
# String retrieved through File IO is encoded in the
-
# <tt>default_external</tt> encoding. In Rails, the default
-
# <tt>default_external</tt> encoding is UTF-8.
-
#
-
# As a result, if a user saves their template as ISO-8859-1
-
# (for instance, using a non-Unicode-aware text editor),
-
# and uses characters outside of the ASCII range, their
-
# users will see diamonds with question marks in them in
-
# the browser.
-
#
-
# For the rest of this documentation, when we say "UTF-8",
-
# we mean "UTF-8 or whatever the default_internal encoding
-
# is set to". By default, it will be UTF-8.
-
#
-
# To mitigate this problem, we use a few strategies:
-
# 1. If the source is not valid UTF-8, we raise an exception
-
# when the template is compiled to alert the user
-
# to the problem.
-
# 2. The user can specify the encoding using Ruby-style
-
# encoding comments in any template engine. If such
-
# a comment is supplied, Rails will apply that encoding
-
# to the resulting compiled source returned by the
-
# template handler.
-
# 3. In all cases, we transcode the resulting String to
-
# the UTF-8.
-
#
-
# This means that other parts of Rails can always assume
-
# that templates are encoded in UTF-8, even if the original
-
# source of the template was not UTF-8.
-
#
-
# From a user's perspective, the easiest thing to do is
-
# to save your templates as UTF-8. If you do this, you
-
# do not need to do anything else for things to "just work".
-
#
-
# === Instructions for template handlers
-
#
-
# The easiest thing for you to do is to simply ignore
-
# encodings. Rails will hand you the template source
-
# as the default_internal (generally UTF-8), raising
-
# an exception for the user before sending the template
-
# to you if it could not determine the original encoding.
-
#
-
# For the greatest simplicity, you can support only
-
# UTF-8 as the <tt>default_internal</tt>. This means
-
# that from the perspective of your handler, the
-
# entire pipeline is just UTF-8.
-
#
-
# === Advanced: Handlers with alternate metadata sources
-
#
-
# If you want to provide an alternate mechanism for
-
# specifying encodings (like ERB does via <%# encoding: ... %>),
-
# you may indicate that you will handle encodings yourself
-
# by implementing <tt>self.handles_encoding?</tt>
-
# on your handler.
-
#
-
# If you do, Rails will not try to encode the String
-
# into the default_internal, passing you the unaltered
-
# bytes tagged with the assumed encoding (from
-
# default_external).
-
#
-
# In this case, make sure you return a String from
-
# your handler encoded in the default_internal. Since
-
# you are handling out-of-band metadata, you are
-
# also responsible for alerting the user to any
-
# problems with converting the user's data to
-
# the <tt>default_internal</tt>.
-
#
-
# To do so, simply raise the raise +WrongEncodingError+
-
# as follows:
-
#
-
# raise WrongEncodingError.new(
-
# problematic_string,
-
# expected_encoding
-
# )
-
-
1
eager_autoload do
-
1
autoload :Error
-
1
autoload :Handlers
-
1
autoload :Text
-
1
autoload :Types
-
end
-
-
1
extend Template::Handlers
-
-
1
attr_accessor :locals, :formats, :virtual_path
-
-
1
attr_reader :source, :identifier, :handler, :original_encoding, :updated_at
-
-
# This finalizer is needed (and exactly with a proc inside another proc)
-
# otherwise templates leak in development.
-
1
Finalizer = proc do |method_name, mod|
-
66
proc do
-
16
mod.module_eval do
-
16
remove_possible_method method_name
-
end
-
end
-
end
-
-
1
def initialize(source, identifier, handler, details)
-
68
format = details[:format] || (handler.default_format if handler.respond_to?(:default_format))
-
-
68
@source = source
-
68
@identifier = identifier
-
68
@handler = handler
-
68
@compiled = false
-
68
@original_encoding = nil
-
68
@locals = details[:locals] || []
-
68
@virtual_path = details[:virtual_path]
-
68
@updated_at = details[:updated_at] || Time.now
-
105
@formats = Array(format).map { |f| f.respond_to?(:ref) ? f.ref : f }
-
68
@compile_mutex = Mutex.new
-
end
-
-
# Returns if the underlying handler supports streaming. If so,
-
# a streaming buffer *may* be passed when it start rendering.
-
1
def supports_streaming?
-
handler.respond_to?(:supports_streaming?) && handler.supports_streaming?
-
end
-
-
# Render a template. If the template was not compiled yet, it is done
-
# exactly before rendering.
-
#
-
# This method is instrumented as "!render_template.action_view". Notice that
-
# we use a bang in this instrumentation because you don't want to
-
# consume this in production. This is only slow if it's being listened to.
-
1
def render(view, locals, buffer=nil, &block)
-
133
ActiveSupport::Notifications.instrument("!render_template.action_view", :virtual_path => @virtual_path) do
-
133
compile!(view)
-
133
view.send(method_name, locals, buffer, &block)
-
end
-
rescue Exception => e
-
handle_render_error(view, e)
-
end
-
-
1
def mime_type
-
message = 'Template#mime_type is deprecated and will be removed in Rails 4.1. Please use type method instead.'
-
ActiveSupport::Deprecation.warn message
-
@mime_type ||= Mime::Type.lookup_by_extension(@formats.first.to_s) if @formats.first
-
end
-
-
1
def type
-
89
@type ||= Types[@formats.first] if @formats.first
-
end
-
-
# Receives a view object and return a template similar to self by using @virtual_path.
-
#
-
# This method is useful if you have a template object but it does not contain its source
-
# anymore since it was already compiled. In such cases, all you need to do is to call
-
# refresh passing in the view object.
-
#
-
# Notice this method raises an error if the template to be refreshed does not have a
-
# virtual path set (true just for inline templates).
-
1
def refresh(view)
-
raise "A template needs to have a virtual path in order to be refreshed" unless @virtual_path
-
lookup = view.lookup_context
-
pieces = @virtual_path.split("/")
-
name = pieces.pop
-
partial = !!name.sub!(/^_/, "")
-
lookup.disable_cache do
-
lookup.find_template(name, [ pieces.join('/') ], partial, @locals)
-
end
-
end
-
-
1
def inspect
-
66
@inspect ||= defined?(Rails.root) ? identifier.sub("#{Rails.root}/", '') : identifier
-
end
-
-
# This method is responsible for properly setting the encoding of the
-
# source. Until this point, we assume that the source is BINARY data.
-
# If no additional information is supplied, we assume the encoding is
-
# the same as <tt>Encoding.default_external</tt>.
-
#
-
# The user can also specify the encoding via a comment on the first
-
# line of the template (# encoding: NAME-OF-ENCODING). This will work
-
# with any template engine, as we process out the encoding comment
-
# before passing the source on to the template engine, leaving a
-
# blank line in its stead.
-
1
def encode!
-
66
return unless source.encoding == Encoding::BINARY
-
-
# Look for # encoding: *. If we find one, we'll encode the
-
# String in that encoding, otherwise, we'll use the
-
# default external encoding.
-
47
if source.sub!(/\A#{ENCODING_FLAG}/, '')
-
encoding = magic_encoding = $1
-
else
-
47
encoding = Encoding.default_external
-
end
-
-
# Tag the source with the default external encoding
-
# or the encoding specified in the file
-
47
source.force_encoding(encoding)
-
-
# If the user didn't specify an encoding, and the handler
-
# handles encodings, we simply pass the String as is to
-
# the handler (with the default_external tag)
-
47
if !magic_encoding && @handler.respond_to?(:handles_encoding?) && @handler.handles_encoding?
-
47
source
-
# Otherwise, if the String is valid in the encoding,
-
# encode immediately to default_internal. This means
-
# that if a handler doesn't handle encodings, it will
-
# always get Strings in the default_internal
-
elsif source.valid_encoding?
-
source.encode!
-
# Otherwise, since the String is invalid in the encoding
-
# specified, raise an exception
-
else
-
raise WrongEncodingError.new(source, encoding)
-
end
-
end
-
-
1
protected
-
-
# Compile a template. This method ensures a template is compiled
-
# just once and removes the source after it is compiled.
-
1
def compile!(view) #:nodoc:
-
133
return if @compiled
-
-
# Templates can be used concurrently in threaded environments
-
# so compilation and any instance variable modification must
-
# be synchronized
-
66
@compile_mutex.synchronize do
-
# Any thread holding this lock will be compiling the template needed
-
# by the threads waiting. So re-check the @compiled flag to avoid
-
# re-compilation
-
66
return if @compiled
-
-
66
if view.is_a?(ActionView::CompiledTemplates)
-
66
mod = ActionView::CompiledTemplates
-
else
-
mod = view.singleton_class
-
end
-
-
66
compile(view, mod)
-
-
# Just discard the source if we have a virtual path. This
-
# means we can get the template back.
-
66
@source = nil if @virtual_path
-
66
@compiled = true
-
end
-
end
-
-
# Among other things, this method is responsible for properly setting
-
# the encoding of the compiled template.
-
#
-
# If the template engine handles encodings, we send the encoded
-
# String to the engine without further processing. This allows
-
# the template engine to support additional mechanisms for
-
# specifying the encoding. For instance, ERB supports <%# encoding: %>
-
#
-
# Otherwise, after we figure out the correct encoding, we then
-
# encode the source into <tt>Encoding.default_internal</tt>.
-
# In general, this means that templates will be UTF-8 inside of Rails,
-
# regardless of the original source encoding.
-
1
def compile(view, mod) #:nodoc:
-
66
encode!
-
66
method_name = self.method_name
-
66
code = @handler.call(self)
-
-
# Make sure that the resulting String to be evalled is in the
-
# encoding of the code
-
66
source = <<-end_src
-
def #{method_name}(local_assigns, output_buffer)
-
_old_virtual_path, @virtual_path = @virtual_path, #{@virtual_path.inspect};_old_output_buffer = @output_buffer;#{locals_code};#{code}
-
ensure
-
@virtual_path, @output_buffer = _old_virtual_path, _old_output_buffer
-
end
-
end_src
-
-
# Make sure the source is in the encoding of the returned code
-
66
source.force_encoding(code.encoding)
-
-
# In case we get back a String from a handler that is not in
-
# BINARY or the default_internal, encode it to the default_internal
-
66
source.encode!
-
-
# Now, validate that the source we got back from the template
-
# handler is valid in the default_internal. This is for handlers
-
# that handle encoding but screw up
-
66
unless source.valid_encoding?
-
raise WrongEncodingError.new(@source, Encoding.default_internal)
-
end
-
-
66
begin
-
66
mod.module_eval(source, identifier, 0)
-
66
ObjectSpace.define_finalizer(self, Finalizer[method_name, mod])
-
rescue Exception => e # errors from template code
-
if logger = (view && view.logger)
-
logger.debug "ERROR: compiling #{method_name} RAISED #{e}"
-
logger.debug "Function body: #{source}"
-
logger.debug "Backtrace: #{e.backtrace.join("\n")}"
-
end
-
-
raise ActionView::Template::Error.new(self, e)
-
end
-
end
-
-
1
def handle_render_error(view, e) #:nodoc:
-
if e.is_a?(Template::Error)
-
e.sub_template_of(self)
-
raise e
-
else
-
template = self
-
unless template.source
-
template = refresh(view)
-
template.encode!
-
end
-
raise Template::Error.new(template, e)
-
end
-
end
-
-
1
def locals_code #:nodoc:
-
66
@locals.map { |key| "#{key} = local_assigns[:#{key}];" }.join
-
end
-
-
1
def method_name #:nodoc:
-
199
@method_name ||= "_#{identifier_method_name}__#{@identifier.hash}_#{__id__}".gsub('-', "_")
-
end
-
-
1
def identifier_method_name #:nodoc:
-
66
inspect.gsub(/[^a-z_]/, '_')
-
end
-
end
-
end
-
1
require "active_support/core_ext/enumerable"
-
-
1
module ActionView
-
# = Action View Errors
-
1
class ActionViewError < StandardError #:nodoc:
-
end
-
-
1
class EncodingError < StandardError #:nodoc:
-
end
-
-
1
class MissingRequestError < StandardError #:nodoc:
-
end
-
-
1
class WrongEncodingError < EncodingError #:nodoc:
-
1
def initialize(string, encoding)
-
@string, @encoding = string, encoding
-
end
-
-
1
def message
-
@string.force_encoding("BINARY")
-
"Your template was not saved as valid #{@encoding}. Please " \
-
"either specify #{@encoding} as the encoding for your template " \
-
"in your text editor, or mark the template with its " \
-
"encoding by inserting the following as the first line " \
-
"of the template:\n\n# encoding: <name of correct encoding>.\n\n" \
-
"The source of your template was:\n\n#{@string}"
-
end
-
end
-
-
1
class MissingTemplate < ActionViewError #:nodoc:
-
1
attr_reader :path
-
-
1
def initialize(paths, path, prefixes, partial, details, *)
-
1
@path = path
-
1
prefixes = Array(prefixes)
-
1
template_type = if partial
-
"partial"
-
elsif path =~ /layouts/i
-
'layout'
-
else
-
1
'template'
-
end
-
-
2
searched_paths = prefixes.map { |prefix| [prefix, path].join("/") }
-
-
1
out = "Missing #{template_type} #{searched_paths.join(", ")} with #{details.inspect}. Searched in:\n"
-
2
out += paths.compact.map { |p| " * #{p.to_s.inspect}\n" }.join
-
1
super out
-
end
-
end
-
-
1
class Template
-
# The Template::Error exception is raised when the compilation or rendering of the template
-
# fails. This exception then gathers a bunch of intimate details and uses it to report a
-
# precise exception message.
-
1
class Error < ActionViewError #:nodoc:
-
1
SOURCE_CODE_RADIUS = 3
-
-
1
attr_reader :original_exception, :backtrace
-
-
1
def initialize(template, original_exception)
-
super(original_exception.message)
-
@template, @original_exception = template, original_exception
-
@sub_templates = nil
-
@backtrace = original_exception.backtrace
-
end
-
-
1
def file_name
-
@template.identifier
-
end
-
-
1
def sub_template_message
-
if @sub_templates
-
"Trace of template inclusion: " +
-
@sub_templates.collect { |template| template.inspect }.join(", ")
-
else
-
""
-
end
-
end
-
-
1
def source_extract(indentation = 0)
-
return unless num = line_number
-
num = num.to_i
-
-
source_code = @template.source.split("\n")
-
-
start_on_line = [ num - SOURCE_CODE_RADIUS - 1, 0 ].max
-
end_on_line = [ num + SOURCE_CODE_RADIUS - 1, source_code.length].min
-
-
indent = end_on_line.to_s.size + indentation
-
line_counter = start_on_line
-
return unless source_code = source_code[start_on_line..end_on_line]
-
-
source_code.sum do |line|
-
line_counter += 1
-
"%#{indent}s: %s\n" % [line_counter, line]
-
end
-
end
-
-
1
def sub_template_of(template_path)
-
@sub_templates ||= []
-
@sub_templates << template_path
-
end
-
-
1
def line_number
-
@line_number ||=
-
if file_name
-
regexp = /#{Regexp.escape File.basename(file_name)}:(\d+)/
-
$1 if message =~ regexp || backtrace.find { |line| line =~ regexp }
-
end
-
end
-
-
1
def annoted_source_code
-
source_extract(4)
-
end
-
-
1
private
-
-
1
def source_location
-
if line_number
-
"on line ##{line_number} of "
-
else
-
'in '
-
end + file_name
-
end
-
end
-
end
-
-
1
TemplateError = Template::Error
-
end
-
1
module ActionView #:nodoc:
-
# = Action View Template Handlers
-
1
class Template
-
1
module Handlers #:nodoc:
-
1
autoload :ERB, 'action_view/template/handlers/erb'
-
1
autoload :Builder, 'action_view/template/handlers/builder'
-
1
autoload :Raw, 'action_view/template/handlers/raw'
-
-
1
def self.extended(base)
-
1
base.register_default_template_handler :erb, ERB.new
-
1
base.register_template_handler :builder, Builder.new
-
1
base.register_template_handler :raw, Raw.new
-
1
base.register_template_handler :ruby, :source.to_proc
-
end
-
-
1
@@template_handlers = {}
-
1
@@default_template_handlers = nil
-
-
1
def self.extensions
-
121
@@template_extensions ||= @@template_handlers.keys
-
end
-
-
# Register an object that knows how to handle template files with the given
-
# extensions. This can be used to implement new template types.
-
# The handler must respond to `:call`, which will be passed the template
-
# and should return the rendered template as a String.
-
1
def register_template_handler(*extensions, handler)
-
6
raise(ArgumentError, "Extension is required") if extensions.empty?
-
6
extensions.each do |extension|
-
6
@@template_handlers[extension.to_sym] = handler
-
end
-
6
@@template_extensions = nil
-
end
-
-
1
def template_handler_extensions
-
@@template_handlers.keys.map {|key| key.to_s }.sort
-
end
-
-
1
def registered_template_handler(extension)
-
68
extension && @@template_handlers[extension.to_sym]
-
end
-
-
1
def register_default_template_handler(extension, klass)
-
1
register_template_handler(extension, klass)
-
1
@@default_template_handlers = klass
-
end
-
-
1
def handler_for_extension(extension)
-
68
registered_template_handler(extension) || @@default_template_handlers
-
end
-
end
-
end
-
end
-
1
module ActionView
-
1
module Template::Handlers
-
1
class Builder
-
# Default format used by Builder.
-
1
class_attribute :default_format
-
1
self.default_format = :xml
-
-
1
def call(template)
-
require_engine
-
"xml = ::Builder::XmlMarkup.new(:indent => 2);" +
-
"self.output_buffer = xml.target!;" +
-
template.source +
-
";xml.target!;"
-
end
-
-
1
protected
-
-
1
def require_engine
-
@required ||= begin
-
require "builder"
-
true
-
end
-
end
-
end
-
end
-
end
-
1
require 'action_dispatch/http/mime_type'
-
1
require 'erubis'
-
-
1
module ActionView
-
1
class Template
-
1
module Handlers
-
1
class Erubis < ::Erubis::Eruby
-
1
def add_preamble(src)
-
66
src << "@output_buffer = output_buffer || ActionView::OutputBuffer.new;"
-
end
-
-
1
def add_text(src, text)
-
92
return if text.empty?
-
54
src << "@output_buffer.safe_concat('" << escape_text(text) << "');"
-
end
-
-
1
BLOCK_EXPR = /\s+(do|\{)(\s*\|[^|]*\|)?\s*\Z/
-
-
1
def add_expr_literal(src, code)
-
35
if code =~ BLOCK_EXPR
-
src << '@output_buffer.append= ' << code
-
else
-
35
src << '@output_buffer.append= (' << code << ');'
-
end
-
end
-
-
1
def add_expr_escaped(src, code)
-
if code =~ BLOCK_EXPR
-
src << "@output_buffer.safe_append= " << code
-
else
-
src << "@output_buffer.safe_concat((" << code << ").to_s);"
-
end
-
end
-
-
1
def add_postamble(src)
-
66
src << '@output_buffer.to_s'
-
end
-
end
-
-
1
class ERB
-
# Specify trim mode for the ERB compiler. Defaults to '-'.
-
# See ERB documentation for suitable values.
-
1
class_attribute :erb_trim_mode
-
1
self.erb_trim_mode = '-'
-
-
# Default implementation used.
-
1
class_attribute :erb_implementation
-
1
self.erb_implementation = Erubis
-
-
1
ENCODING_TAG = Regexp.new("\\A(<%#{ENCODING_FLAG}-?%>)[ \\t]*")
-
-
1
def self.call(template)
-
new.call(template)
-
end
-
-
1
def supports_streaming?
-
true
-
end
-
-
1
def handles_encoding?
-
47
true
-
end
-
-
1
def call(template)
-
# First, convert to BINARY, so in case the encoding is
-
# wrong, we can still find an encoding tag
-
# (<%# encoding %>) inside the String using a regular
-
# expression
-
66
template_source = template.source.dup.force_encoding("BINARY")
-
-
66
erb = template_source.gsub(ENCODING_TAG, '')
-
66
encoding = $2
-
-
66
erb.force_encoding valid_encoding(template.source.dup, encoding)
-
-
# Always make sure we return a String in the default_internal
-
66
erb.encode!
-
-
66
self.class.erb_implementation.new(
-
erb,
-
66
:trim => (self.class.erb_trim_mode == "-")
-
).src
-
end
-
-
1
private
-
-
1
def valid_encoding(string, encoding)
-
# If a magic encoding comment was found, tag the
-
# String with this encoding. This is for a case
-
# where the original String was assumed to be,
-
# for instance, UTF-8, but a magic comment
-
# proved otherwise
-
66
string.force_encoding(encoding) if encoding
-
-
# If the String is valid, return the encoding we found
-
66
return string.encoding if string.valid_encoding?
-
-
# Otherwise, raise an exception
-
raise WrongEncodingError.new(string, string.encoding)
-
end
-
end
-
end
-
end
-
end
-
1
module ActionView
-
1
module Template::Handlers
-
1
class Raw
-
1
def call(template)
-
escaped = template.source.gsub(':', '\:')
-
-
'%q:' + escaped + ':;'
-
end
-
end
-
end
-
end
-
1
require "pathname"
-
1
require "active_support/core_ext/class"
-
1
require "active_support/core_ext/class/attribute_accessors"
-
1
require "action_view/template"
-
1
require "thread"
-
1
require "mutex_m"
-
-
1
module ActionView
-
# = Action View Resolver
-
1
class Resolver
-
# Keeps all information about view path and builds virtual path.
-
1
class Path
-
1
attr_reader :name, :prefix, :partial, :virtual
-
1
alias_method :partial?, :partial
-
-
1
def self.build(name, prefix, partial)
-
70
virtual = ""
-
70
virtual << "#{prefix}/" unless prefix.empty?
-
70
virtual << (partial ? "_#{name}" : name)
-
70
new name, prefix, partial, virtual
-
end
-
-
1
def initialize(name, prefix, partial, virtual)
-
70
@name = name
-
70
@prefix = prefix
-
70
@partial = partial
-
70
@virtual = virtual
-
end
-
-
1
def to_str
-
70
@virtual
-
end
-
1
alias :to_s :to_str
-
end
-
-
# Threadsafe template cache
-
1
class Cache #:nodoc:
-
1
class CacheEntry
-
1
include Mutex_m
-
-
1
attr_accessor :templates
-
end
-
-
1
def initialize
-
17
@data = Hash.new { |h1,k1| h1[k1] = Hash.new { |h2,k2|
-
198
h2[k2] = Hash.new { |h3,k3| h3[k3] = Hash.new { |h4,k4| h4[k4] = {} } } } }
-
5
@mutex = Mutex.new
-
end
-
-
# Cache the templates returned by the block
-
1
def cache(key, name, prefix, partial, locals)
-
305
cache_entry = nil
-
-
# first obtain a lock on the main data structure to create the cache entry
-
305
@mutex.synchronize do
-
305
cache_entry = @data[key][name][prefix][partial][locals] ||= CacheEntry.new
-
end
-
-
# then to avoid a long lasting global lock, obtain a more granular lock
-
# on the CacheEntry itself
-
305
cache_entry.synchronize do
-
305
if Resolver.caching?
-
305
cache_entry.templates ||= yield
-
else
-
fresh_templates = yield
-
-
if templates_have_changed?(cache_entry.templates, fresh_templates)
-
cache_entry.templates = fresh_templates
-
else
-
cache_entry.templates ||= []
-
end
-
end
-
end
-
end
-
-
1
def clear
-
@mutex.synchronize do
-
@data.clear
-
end
-
end
-
-
1
private
-
-
1
def templates_have_changed?(cached_templates, fresh_templates)
-
# if either the old or new template list is empty, we don't need to (and can't)
-
# compare modification times, and instead just check whether the lists are different
-
if cached_templates.blank? || fresh_templates.blank?
-
return fresh_templates.blank? != cached_templates.blank?
-
end
-
-
cached_templates_max_updated_at = cached_templates.map(&:updated_at).max
-
-
# if a template has changed, it will be now be newer than all the cached templates
-
fresh_templates.any? { |t| t.updated_at > cached_templates_max_updated_at }
-
end
-
end
-
-
1
cattr_accessor :caching
-
1
self.caching = true
-
-
1
class << self
-
1
alias :caching? :caching
-
end
-
-
1
def initialize
-
5
@cache = Cache.new
-
end
-
-
1
def clear_cache
-
@cache.clear
-
end
-
-
# Normalizes the arguments and passes it on to find_template.
-
1
def find_all(name, prefix=nil, partial=false, details={}, key=nil, locals=[])
-
305
cached(key, [name, prefix, partial], details, locals) do
-
70
find_templates(name, prefix, partial, details)
-
end
-
end
-
-
1
private
-
-
1
delegate :caching?, :to => "self.class"
-
-
# This is what child classes implement. No defaults are needed
-
# because Resolver guarantees that the arguments are present and
-
# normalized.
-
1
def find_templates(name, prefix, partial, details)
-
raise NotImplementedError, "Subclasses must implement a find_templates(name, prefix, partial, details) method"
-
end
-
-
# Helpers that builds a path. Useful for building virtual paths.
-
1
def build_path(name, prefix, partial)
-
Path.build(name, prefix, partial)
-
end
-
-
# Handles templates caching. If a key is given and caching is on
-
# always check the cache before hitting the resolver. Otherwise,
-
# it always hits the resolver but if the key is present, check if the
-
# resolver is fresher before returning it.
-
1
def cached(key, path_info, details, locals) #:nodoc:
-
305
name, prefix, partial = path_info
-
305
locals = locals.map { |x| x.to_s }.sort!
-
-
305
if key
-
305
@cache.cache(key, name, prefix, partial, locals) do
-
70
decorate(yield, path_info, details, locals)
-
end
-
else
-
decorate(yield, path_info, details, locals)
-
end
-
end
-
-
# Ensures all the resolver information is set in the template.
-
1
def decorate(templates, path_info, details, locals) #:nodoc:
-
70
cached = nil
-
70
templates.each do |t|
-
49
t.locals = locals
-
49
t.formats = details[:formats] || [:html] if t.formats.empty?
-
49
t.virtual_path ||= (cached ||= build_path(*path_info))
-
end
-
end
-
end
-
-
# An abstract class that implements a Resolver with path semantics.
-
1
class PathResolver < Resolver #:nodoc:
-
1
EXTENSIONS = [:locale, :formats, :handlers]
-
1
DEFAULT_PATTERN = ":prefix/:action{.:locale,}{.:formats,}{.:handlers,}"
-
-
1
def initialize(pattern=nil)
-
5
@pattern = pattern || DEFAULT_PATTERN
-
5
super()
-
end
-
-
1
private
-
-
1
def find_templates(name, prefix, partial, details)
-
70
path = Path.build(name, prefix, partial)
-
70
query(path, details, details[:formats])
-
end
-
-
1
def query(path, details, formats)
-
70
query = build_query(path, details)
-
-
# deals with case-insensitive file systems.
-
113
sanitizer = Hash.new { |h,dir| h[dir] = Dir["#{dir}/*"] }
-
-
70
template_paths = Dir[query].reject { |filename|
-
File.directory?(filename) ||
-
49
!sanitizer[File.dirname(filename)].include?(filename)
-
}
-
-
70
template_paths.map { |template|
-
49
handler, format = extract_handler_and_format(template, formats)
-
49
contents = File.binread template
-
-
49
Template.new(contents, File.expand_path(template), handler,
-
:virtual_path => path.virtual,
-
:format => format,
-
:updated_at => mtime(template))
-
}
-
end
-
-
# Helper for building query glob string based on resolver's pattern.
-
1
def build_query(path, details)
-
query = @pattern.dup
-
-
prefix = path.prefix.empty? ? "" : "#{escape_entry(path.prefix)}\\1"
-
query.gsub!(/\:prefix(\/)?/, prefix)
-
-
partial = escape_entry(path.partial? ? "_#{path.name}" : path.name)
-
query.gsub!(/\:action/, partial)
-
-
details.each do |ext, variants|
-
query.gsub!(/\:#{ext}/, "{#{variants.compact.uniq.join(',')}}")
-
end
-
-
File.expand_path(query, @path)
-
end
-
-
1
def escape_entry(entry)
-
70
entry.gsub(/[*?{}\[\]]/, '\\\\\\&')
-
end
-
-
# Returns the file mtime from the filesystem.
-
1
def mtime(p)
-
49
File.mtime(p)
-
end
-
-
# Extract handler and formats from path. If a format cannot be a found neither
-
# from the path, or the handler, we should return the array of formats given
-
# to the resolver.
-
1
def extract_handler_and_format(path, default_formats)
-
49
pieces = File.basename(path).split(".")
-
49
pieces.shift
-
-
49
extension = pieces.pop
-
49
unless extension
-
message = "The file #{path} did not specify a template handler. The default is currently ERB, " \
-
"but will change to RAW in the future."
-
ActiveSupport::Deprecation.warn message
-
end
-
-
49
handler = Template.handler_for_extension(extension)
-
49
format = pieces.last && Template::Types[pieces.last]
-
49
[handler, format]
-
end
-
end
-
-
# A resolver that loads files from the filesystem. It allows setting your own
-
# resolving pattern. Such pattern can be a glob string supported by some variables.
-
#
-
# ==== Examples
-
#
-
# Default pattern, loads views the same way as previous versions of rails, eg. when you're
-
# looking for `users/new` it will produce query glob: `users/new{.{en},}{.{html,js},}{.{erb,haml},}`
-
#
-
# FileSystemResolver.new("/path/to/views", ":prefix/:action{.:locale,}{.:formats,}{.:handlers,}")
-
#
-
# This one allows you to keep files with different formats in seperated subdirectories,
-
# eg. `users/new.html` will be loaded from `users/html/new.erb` or `users/new.html.erb`,
-
# `users/new.js` from `users/js/new.erb` or `users/new.js.erb`, etc.
-
#
-
# FileSystemResolver.new("/path/to/views", ":prefix/{:formats/,}:action{.:locale,}{.:formats,}{.:handlers,}")
-
#
-
# If you don't specify a pattern then the default will be used.
-
#
-
# In order to use any of the customized resolvers above in a Rails application, you just need
-
# to configure ActionController::Base.view_paths in an initializer, for example:
-
#
-
# ActionController::Base.view_paths = FileSystemResolver.new(
-
# Rails.root.join("app/views"),
-
# ":prefix{/:locale}/:action{.:formats,}{.:handlers,}"
-
# )
-
#
-
# ==== Pattern format and variables
-
#
-
# Pattern has to be a valid glob string, and it allows you to use the
-
# following variables:
-
#
-
# * <tt>:prefix</tt> - usually the controller path
-
# * <tt>:action</tt> - name of the action
-
# * <tt>:locale</tt> - possible locale versions
-
# * <tt>:formats</tt> - possible request formats (for example html, json, xml...)
-
# * <tt>:handlers</tt> - possible handlers (for example erb, haml, builder...)
-
#
-
1
class FileSystemResolver < PathResolver
-
1
def initialize(path, pattern=nil)
-
5
raise ArgumentError, "path already is a Resolver class" if path.is_a?(Resolver)
-
5
super(pattern)
-
5
@path = File.expand_path(path)
-
end
-
-
1
def to_s
-
@path.to_s
-
end
-
1
alias :to_path :to_s
-
-
1
def eql?(resolver)
-
self.class.equal?(resolver.class) && to_path == resolver.to_path
-
end
-
1
alias :== :eql?
-
end
-
-
# An Optimized resolver for Rails' most common case.
-
1
class OptimizedFileSystemResolver < FileSystemResolver #:nodoc:
-
1
def build_query(path, details)
-
280
exts = EXTENSIONS.map { |ext| details[ext] }
-
70
query = escape_entry(File.join(@path, path))
-
-
query + exts.map { |ext|
-
1370
"{#{ext.compact.uniq.map { |e| ".#{e}," }.join}}"
-
70
}.join
-
end
-
end
-
-
# The same as FileSystemResolver but does not allow templates to store
-
# a virtual path since it is invalid for such resolvers.
-
1
class FallbackFileSystemResolver < FileSystemResolver #:nodoc:
-
1
def self.instances
-
1
[new(""), new("/")]
-
end
-
-
1
def decorate(*)
-
super.each { |t| t.virtual_path = nil }
-
end
-
end
-
end
-
1
module ActionView #:nodoc:
-
# = Action View Text Template
-
1
class Template
-
1
class Text #:nodoc:
-
1
attr_accessor :type
-
-
1
def initialize(string, type = nil)
-
13
@string = string.to_s
-
13
@type = Types[type] || type if type
-
13
@type ||= Types[:text]
-
end
-
-
1
def identifier
-
13
'text template'
-
end
-
-
1
def inspect
-
'text template'
-
end
-
-
1
def to_str
-
13
@string
-
end
-
-
1
def render(*args)
-
13
to_str
-
end
-
-
1
def formats
-
20
[@type.to_sym]
-
end
-
end
-
end
-
end
-
1
require 'set'
-
1
require 'active_support/core_ext/class/attribute_accessors'
-
1
require 'active_support/core_ext/object/blank'
-
-
1
module ActionView
-
1
class Template
-
1
class Types
-
1
class Type
-
1
cattr_accessor :types
-
1
self.types = Set.new
-
-
1
def self.register(*t)
-
7
types.merge(t.map { |type| type.to_s })
-
end
-
-
1
register :html, :text, :js, :css, :xml, :json
-
-
1
def self.[](type)
-
return type if type.is_a?(self)
-
-
if type.is_a?(Symbol) || types.member?(type.to_s)
-
new(type)
-
end
-
end
-
-
1
attr_reader :symbol
-
-
1
def initialize(symbol)
-
@symbol = symbol.to_sym
-
end
-
-
1
delegate :to_s, :to_sym, :to => :symbol
-
1
alias to_str to_s
-
-
1
def ref
-
to_sym || to_s
-
end
-
-
1
def ==(type)
-
return false if type.blank?
-
symbol.to_sym == type.to_sym
-
end
-
end
-
-
1
cattr_accessor :type_klass
-
-
1
def self.delegate_to(klass)
-
2
self.type_klass = klass
-
end
-
-
1
delegate_to Type
-
-
1
def self.[](type)
-
81
type_klass[type]
-
end
-
end
-
end
-
end
-
1
$LOAD_PATH << "#{File.dirname(__FILE__)}/html-scanner"
-
-
1
module HTML
-
1
extend ActiveSupport::Autoload
-
-
1
eager_autoload do
-
1
autoload :CDATA, 'html/node'
-
1
autoload :Document, 'html/document'
-
1
autoload :FullSanitizer, 'html/sanitizer'
-
1
autoload :LinkSanitizer, 'html/sanitizer'
-
1
autoload :Node, 'html/node'
-
1
autoload :Sanitizer, 'html/sanitizer'
-
1
autoload :Selector, 'html/selector'
-
1
autoload :Tag, 'html/node'
-
1
autoload :Text, 'html/node'
-
1
autoload :Tokenizer, 'html/tokenizer'
-
1
autoload :Version, 'html/version'
-
1
autoload :WhiteListSanitizer, 'html/sanitizer'
-
end
-
end
-
#--
-
# Copyright (c) 2005-2012 David Heinemeier Hansson
-
#
-
# Permission is hereby granted, free of charge, to any person obtaining
-
# a copy of this software and associated documentation files (the
-
# "Software"), to deal in the Software without restriction, including
-
# without limitation the rights to use, copy, modify, merge, publish,
-
# distribute, sublicense, and/or sell copies of the Software, and to
-
# permit persons to whom the Software is furnished to do so, subject to
-
# the following conditions:
-
#
-
# The above copyright notice and this permission notice shall be
-
# included in all copies or substantial portions of the Software.
-
#
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
#++
-
-
1
require 'securerandom'
-
1
require "active_support/dependencies/autoload"
-
1
require "active_support/version"
-
1
require "active_support/logger"
-
1
require "active_support/lazy_load_hooks"
-
-
1
module ActiveSupport
-
1
extend ActiveSupport::Autoload
-
-
1
autoload :Concern
-
1
autoload :Dependencies
-
1
autoload :DescendantsTracker
-
1
autoload :FileUpdateChecker
-
1
autoload :LogSubscriber
-
1
autoload :Notifications
-
-
1
eager_autoload do
-
1
autoload :BacktraceCleaner
-
1
autoload :BasicObject
-
1
autoload :Benchmarkable
-
1
autoload :Cache
-
1
autoload :Callbacks
-
1
autoload :Configurable
-
1
autoload :Deprecation
-
1
autoload :Gzip
-
1
autoload :Inflector
-
1
autoload :JSON
-
1
autoload :KeyGenerator
-
1
autoload :MessageEncryptor
-
1
autoload :MessageVerifier
-
1
autoload :Multibyte
-
1
autoload :OptionMerger
-
1
autoload :OrderedHash
-
1
autoload :OrderedOptions
-
1
autoload :StringInquirer
-
1
autoload :TaggedLogging
-
1
autoload :XmlMini
-
end
-
-
1
autoload :Rescuable
-
1
autoload :SafeBuffer, "active_support/core_ext/string/output_safety"
-
1
autoload :TestCase
-
end
-
-
1
autoload :I18n, "active_support/i18n"
-
1
module ActiveSupport
-
# A class with no predefined methods that behaves similarly to Builder's
-
# BlankSlate. Used for proxy classes.
-
1
class BasicObject < ::BasicObject
-
1
undef_method :==
-
1
undef_method :equal?
-
-
# Let ActiveSupport::BasicObject at least raise exceptions.
-
1
def raise(*args)
-
::Object.send(:raise, *args)
-
end
-
end
-
end
-
1
require 'active_support/core_ext/benchmark'
-
1
require 'active_support/core_ext/hash/keys'
-
-
1
module ActiveSupport
-
1
module Benchmarkable
-
# Allows you to measure the execution time of a block in a template and
-
# records the result to the log. Wrap this block around expensive operations
-
# or possible bottlenecks to get a time reading for the operation. For
-
# example, let's say you thought your file processing method was taking too
-
# long; you could wrap it in a benchmark block.
-
#
-
# <% benchmark 'Process data files' do %>
-
# <%= expensive_files_operation %>
-
# <% end %>
-
#
-
# That would add something like "Process data files (345.2ms)" to the log,
-
# which you can then use to compare timings when optimizing your code.
-
#
-
# You may give an optional logger level (<tt>:debug</tt>, <tt>:info</tt>,
-
# <tt>:warn</tt>, <tt>:error</tt>) as the <tt>:level</tt> option. The
-
# default logger level value is <tt>:info</tt>.
-
#
-
# <% benchmark 'Low-level files', level: :debug do %>
-
# <%= lowlevel_files_operation %>
-
# <% end %>
-
#
-
# Finally, you can pass true as the third argument to silence all log
-
# activity (other than the timing information) from inside the block. This
-
# is great for boiling down a noisy block to just a single statement that
-
# produces one log line:
-
#
-
# <% benchmark 'Process data files', level: :info, silence: true do %>
-
# <%= expensive_and_chatty_files_operation %>
-
# <% end %>
-
1
def benchmark(message = "Benchmarking", options = {})
-
if logger
-
options.assert_valid_keys(:level, :silence)
-
options[:level] ||= :info
-
-
result = nil
-
ms = Benchmark.ms { result = options[:silence] ? silence { yield } : yield }
-
logger.send(options[:level], '%s (%.1fms)' % [ message, ms ])
-
result
-
else
-
yield
-
end
-
end
-
-
# Silence the logger during the execution of the block.
-
1
def silence
-
message = "ActiveSupport::Benchmarkable#silence is deprecated. It will be removed from Rails 4.1."
-
ActiveSupport::Deprecation.warn message
-
old_logger_level, logger.level = logger.level, ::Logger::ERROR if logger
-
yield
-
ensure
-
logger.level = old_logger_level if logger
-
end
-
end
-
end
-
1
require 'active_support/deprecation'
-
1
require 'active_support/logger'
-
-
1
module ActiveSupport
-
1
BufferedLogger = ActiveSupport::Deprecation::DeprecatedConstantProxy.new(
-
'BufferedLogger', '::ActiveSupport::Logger')
-
end
-
1
require 'active_support/concern'
-
1
require 'active_support/descendants_tracker'
-
1
require 'active_support/core_ext/class/attribute'
-
1
require 'active_support/core_ext/kernel/reporting'
-
1
require 'active_support/core_ext/kernel/singleton_class'
-
-
1
module ActiveSupport
-
# Callbacks are code hooks that are run at key points in an object's lifecycle.
-
# The typical use case is to have a base class define a set of callbacks
-
# relevant to the other functionality it supplies, so that subclasses can
-
# install callbacks that enhance or modify the base functionality without
-
# needing to override or redefine methods of the base class.
-
#
-
# Mixing in this module allows you to define the events in the object's
-
# lifecycle that will support callbacks (via +ClassMethods.define_callbacks+),
-
# set the instance methods, procs, or callback objects to be called (via
-
# +ClassMethods.set_callback+), and run the installed callbacks at the
-
# appropriate times (via +run_callbacks+).
-
#
-
# Three kinds of callbacks are supported: before callbacks, run before a
-
# certain event; after callbacks, run after the event; and around callbacks,
-
# blocks that surround the event, triggering it when they yield. Callback code
-
# can be contained in instance methods, procs or lambdas, or callback objects
-
# that respond to certain predetermined methods. See +ClassMethods.set_callback+
-
# for details.
-
#
-
# class Record
-
# include ActiveSupport::Callbacks
-
# define_callbacks :save
-
#
-
# def save
-
# run_callbacks :save do
-
# puts "- save"
-
# end
-
# end
-
# end
-
#
-
# class PersonRecord < Record
-
# set_callback :save, :before, :saving_message
-
# def saving_message
-
# puts "saving..."
-
# end
-
#
-
# set_callback :save, :after do |object|
-
# puts "saved"
-
# end
-
# end
-
#
-
# person = PersonRecord.new
-
# person.save
-
#
-
# Output:
-
# saving...
-
# - save
-
# saved
-
1
module Callbacks
-
1
extend Concern
-
-
1
included do
-
3
extend ActiveSupport::DescendantsTracker
-
end
-
-
# Runs the callbacks for the given event.
-
#
-
# Calls the before and around callbacks in the order they were set, yields
-
# the block (if given one), and then runs the after callbacks in reverse
-
# order.
-
#
-
# If the callback chain was halted, returns +false+. Otherwise returns the
-
# result of the block, or +true+ if no block is given.
-
#
-
# run_callbacks :save do
-
# save
-
# end
-
1
def run_callbacks(kind, &block)
-
413
runner_name = self.class.__define_callbacks(kind, self)
-
413
send(runner_name, &block)
-
end
-
-
1
private
-
-
# A hook invoked everytime a before callback is halted.
-
# This can be overridden in AS::Callback implementors in order
-
# to provide better debugging/logging.
-
1
def halted_callback_hook(filter)
-
end
-
-
1
class Callback #:nodoc:#
-
1
@@_callback_sequence = 0
-
-
1
attr_accessor :chain, :filter, :kind, :options, :klass, :raw_filter
-
-
1
def initialize(chain, filter, kind, options, klass)
-
10
@chain, @kind, @klass = chain, kind, klass
-
10
deprecate_per_key_option(options)
-
10
normalize_options!(options)
-
-
10
@raw_filter, @options = filter, options
-
10
@filter = _compile_filter(filter)
-
10
recompile_options!
-
end
-
-
1
def deprecate_per_key_option(options)
-
10
if options[:per_key]
-
raise NotImplementedError, ":per_key option is no longer supported. Use generic :if and :unless options instead."
-
end
-
end
-
-
1
def clone(chain, klass)
-
obj = super()
-
obj.chain = chain
-
obj.klass = klass
-
obj.options = @options.dup
-
obj.options[:if] = @options[:if].dup
-
obj.options[:unless] = @options[:unless].dup
-
obj
-
end
-
-
1
def normalize_options!(options)
-
10
options[:if] = Array(options[:if])
-
10
options[:unless] = Array(options[:unless])
-
end
-
-
1
def name
-
chain.name
-
end
-
-
1
def next_id
-
10
@@_callback_sequence += 1
-
end
-
-
1
def matches?(_kind, _filter)
-
2
@kind == _kind && @filter == _filter
-
end
-
-
1
def _update_filter(filter_options, new_options)
-
filter_options[:if].concat(Array(new_options[:unless])) if new_options.key?(:unless)
-
filter_options[:unless].concat(Array(new_options[:if])) if new_options.key?(:if)
-
end
-
-
1
def recompile!(_options)
-
deprecate_per_key_option(_options)
-
_update_filter(self.options, _options)
-
-
recompile_options!
-
end
-
-
# Wraps code with filter
-
1
def apply(code)
-
57
case @kind
-
when :before
-
<<-RUBY_EVAL
-
55
if !halted && #{@compiled_options}
-
# This double assignment is to prevent warnings in 1.9.3 as
-
# the `result` variable is not always used except if the
-
# terminator code refers to it.
-
result = result = #{@filter}
-
halted = (#{chain.config[:terminator]})
-
if halted
-
halted_callback_hook(#{@raw_filter.inspect.inspect})
-
end
-
end
-
#{code}
-
RUBY_EVAL
-
when :after
-
<<-RUBY_EVAL
-
2
#{code}
-
if #{!chain.config[:skip_after_callbacks_if_terminated] || "!halted"} && #{@compiled_options}
-
#{@filter}
-
end
-
RUBY_EVAL
-
when :around
-
name = define_conditional_callback
-
<<-RUBY_EVAL
-
#{name}(halted) do
-
#{code}
-
value
-
end
-
RUBY_EVAL
-
end
-
end
-
-
1
private
-
-
# Compile around filters with conditions into proxy methods
-
# that contain the conditions.
-
#
-
# For `set_callback :save, :around, :filter_name, if: :condition':
-
#
-
# def _conditional_callback_save_17
-
# if condition
-
# filter_name do
-
# yield self
-
# end
-
# else
-
# yield self
-
# end
-
# end
-
1
def define_conditional_callback
-
name = "_conditional_callback_#{@kind}_#{next_id}"
-
@klass.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
-
def #{name}(halted)
-
if #{@compiled_options} && !halted
-
#{@filter} do
-
yield self
-
end
-
else
-
yield self
-
end
-
end
-
RUBY_EVAL
-
name
-
end
-
-
# Options support the same options as filters themselves (and support
-
# symbols, string, procs, and objects), so compile a conditional
-
# expression based on the options.
-
1
def recompile_options!
-
10
conditions = ["true"]
-
-
10
unless options[:if].empty?
-
conditions << Array(_compile_filter(options[:if]))
-
end
-
-
10
unless options[:unless].empty?
-
conditions << Array(_compile_filter(options[:unless])).map {|f| "!#{f}"}
-
end
-
-
10
@compiled_options = conditions.flatten.join(" && ")
-
end
-
-
# Filters support:
-
#
-
# Arrays:: Used in conditions. This is used to specify
-
# multiple conditions. Used internally to
-
# merge conditions from skip_* filters.
-
# Symbols:: A method to call.
-
# Strings:: Some content to evaluate.
-
# Procs:: A proc to call with the object.
-
# Objects:: An object with a <tt>before_foo</tt> method on it to call.
-
#
-
# All of these objects are compiled into methods and handled
-
# the same after this point:
-
#
-
# Arrays:: Merged together into a single filter.
-
# Symbols:: Already methods.
-
# Strings:: class_eval'ed into methods.
-
# Procs:: define_method'ed into methods.
-
# Objects::
-
# a method is created that calls the before_foo method
-
# on the object.
-
1
def _compile_filter(filter)
-
10
method_name = "_callback_#{@kind}_#{next_id}"
-
10
case filter
-
when Array
-
filter.map {|f| _compile_filter(f)}
-
when Symbol
-
10
filter
-
when String
-
"(#{filter})"
-
when Proc
-
@klass.send(:define_method, method_name, &filter)
-
return method_name if filter.arity <= 0
-
-
method_name << (filter.arity == 1 ? "(self)" : " self, Proc.new ")
-
else
-
@klass.send(:define_method, "#{method_name}_object") { filter }
-
-
_normalize_legacy_filter(kind, filter)
-
scopes = Array(chain.config[:scope])
-
method_to_call = scopes.map{ |s| s.is_a?(Symbol) ? send(s) : s }.join("_")
-
-
@klass.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
-
def #{method_name}(&blk)
-
#{method_name}_object.send(:#{method_to_call}, self, &blk)
-
end
-
RUBY_EVAL
-
-
method_name
-
end
-
end
-
-
1
def _normalize_legacy_filter(kind, filter)
-
if !filter.respond_to?(kind) && filter.respond_to?(:filter)
-
message = "Filter object with #filter method is deprecated. Define method corresponding " \
-
"to filter type (#before, #after or #around)."
-
ActiveSupport::Deprecation.warn message
-
filter.singleton_class.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
-
def #{kind}(context, &block) filter(context, &block) end
-
RUBY_EVAL
-
elsif filter.respond_to?(:before) && filter.respond_to?(:after) && kind == :around && !filter.respond_to?(:around)
-
message = "Filter object with #before and #after methods is deprecated. Define #around method instead."
-
ActiveSupport::Deprecation.warn message
-
def filter.around(context)
-
should_continue = before(context)
-
yield if should_continue
-
after(context)
-
end
-
end
-
end
-
end
-
-
# An Array with a compile method.
-
1
class CallbackChain < Array #:nodoc:#
-
1
attr_reader :name, :config
-
-
1
def initialize(name, config)
-
4
@name = name
-
4
@config = {
-
:terminator => "false",
-
:scope => [ :kind ]
-
}.merge(config)
-
end
-
-
1
def compile
-
86
method = []
-
86
method << "value = nil"
-
86
method << "halted = false"
-
-
86
callbacks = "value = !halted && (!block_given? || yield)"
-
86
reverse_each do |callback|
-
57
callbacks = callback.apply(callbacks)
-
end
-
86
method << callbacks
-
-
86
method << "value"
-
86
method.join("\n")
-
end
-
-
end
-
-
1
module ClassMethods
-
-
# This method defines callback chain method for the given kind
-
# if it was not yet defined.
-
# This generated method plays caching role.
-
1
def __define_callbacks(kind, object) #:nodoc:
-
413
name = __callback_runner_name(kind)
-
413
unless object.respond_to?(name, true)
-
86
str = object.send("_#{kind}_callbacks").compile
-
86
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
-
def #{name}() #{str} end
-
protected :#{name}
-
RUBY_EVAL
-
end
-
413
name
-
end
-
-
1
def __reset_runner(symbol)
-
10
name = __callback_runner_name(symbol)
-
10
undef_method(name) if method_defined?(name)
-
end
-
-
1
def __callback_runner_name(kind)
-
423
"_run__#{self.name.hash.abs}__#{kind}__callbacks"
-
end
-
-
# This is used internally to append, prepend and skip callbacks to the
-
# CallbackChain.
-
1
def __update_callbacks(name, filters = [], block = nil) #:nodoc:
-
10
type = [:before, :after, :around].include?(filters.first) ? filters.shift : :before
-
10
options = filters.last.is_a?(Hash) ? filters.pop : {}
-
10
filters.unshift(block) if block
-
-
10
([self] + ActiveSupport::DescendantsTracker.descendants(self)).reverse.each do |target|
-
10
chain = target.send("_#{name}_callbacks")
-
10
yield target, chain.dup, type, filters, options
-
10
target.__reset_runner(name)
-
end
-
end
-
-
# Install a callback for the given event.
-
#
-
# set_callback :save, :before, :before_meth
-
# set_callback :save, :after, :after_meth, if: :condition
-
# set_callback :save, :around, ->(r, &block) { stuff; result = block.call; stuff }
-
#
-
# The second arguments indicates whether the callback is to be run +:before+,
-
# +:after+, or +:around+ the event. If omitted, +:before+ is assumed. This
-
# means the first example above can also be written as:
-
#
-
# set_callback :save, :before_meth
-
#
-
# The callback can specified as a symbol naming an instance method; as a
-
# proc, lambda, or block; as a string to be instance evaluated; or as an
-
# object that responds to a certain method determined by the <tt>:scope</tt>
-
# argument to +define_callback+.
-
#
-
# If a proc, lambda, or block is given, its body is evaluated in the context
-
# of the current object. It can also optionally accept the current object as
-
# an argument.
-
#
-
# Before and around callbacks are called in the order that they are set;
-
# after callbacks are called in the reverse order.
-
#
-
# Around callbacks can access the return value from the event, if it
-
# wasn't halted, from the +yield+ call.
-
#
-
# ===== Options
-
#
-
# * <tt>:if</tt> - A symbol naming an instance method or a proc; the
-
# callback will be called only when it returns a +true+ value.
-
# * <tt>:unless</tt> - A symbol naming an instance method or a proc; the
-
# callback will be called only when it returns a +false+ value.
-
# * <tt>:prepend</tt> - If +true+, the callback will be prepended to the
-
# existing chain rather than appended.
-
1
def set_callback(name, *filter_list, &block)
-
10
mapped = nil
-
-
10
__update_callbacks(name, filter_list, block) do |target, chain, type, filters, options|
-
mapped ||= filters.map do |filter|
-
10
Callback.new(chain, filter, type, options.dup, self)
-
10
end
-
-
10
filters.each do |filter|
-
12
chain.delete_if {|c| c.matches?(type, filter) }
-
end
-
-
10
options[:prepend] ? chain.unshift(*(mapped.reverse)) : chain.push(*mapped)
-
-
10
target.send("_#{name}_callbacks=", chain)
-
end
-
end
-
-
# Skip a previously set callback. Like +set_callback+, <tt>:if</tt> or
-
# <tt>:unless</tt> options may be passed in order to control when the
-
# callback is skipped.
-
#
-
# class Writer < Person
-
# skip_callback :validate, :before, :check_membership, if: -> { self.age > 18 }
-
# end
-
1
def skip_callback(name, *filter_list, &block)
-
__update_callbacks(name, filter_list, block) do |target, chain, type, filters, options|
-
filters.each do |filter|
-
filter = chain.find {|c| c.matches?(type, filter) }
-
-
if filter && options.any?
-
new_filter = filter.clone(chain, self)
-
chain.insert(chain.index(filter), new_filter)
-
new_filter.recompile!(options)
-
end
-
-
chain.delete(filter)
-
end
-
target.send("_#{name}_callbacks=", chain)
-
end
-
end
-
-
# Remove all set callbacks for the given event.
-
1
def reset_callbacks(symbol)
-
callbacks = send("_#{symbol}_callbacks")
-
-
ActiveSupport::DescendantsTracker.descendants(self).each do |target|
-
chain = target.send("_#{symbol}_callbacks").dup
-
callbacks.each { |c| chain.delete(c) }
-
target.send("_#{symbol}_callbacks=", chain)
-
target.__reset_runner(symbol)
-
end
-
-
self.send("_#{symbol}_callbacks=", callbacks.dup.clear)
-
-
__reset_runner(symbol)
-
end
-
-
# Define sets of events in the object lifecycle that support callbacks.
-
#
-
# define_callbacks :validate
-
# define_callbacks :initialize, :save, :destroy
-
#
-
# ===== Options
-
#
-
# * <tt>:terminator</tt> - Determines when a before filter will halt the
-
# callback chain, preventing following callbacks from being called and
-
# the event from being triggered. This is a string to be eval'ed. The
-
# result of the callback is available in the +result+ variable.
-
#
-
# define_callbacks :validate, terminator: 'result == false'
-
#
-
# In this example, if any before validate callbacks returns +false+,
-
# other callbacks are not executed. Defaults to +false+, meaning no value
-
# halts the chain.
-
#
-
# * <tt>:skip_after_callbacks_if_terminated</tt> - Determines if after
-
# callbacks should be terminated by the <tt>:terminator</tt> option. By
-
# default after callbacks executed no matter if callback chain was
-
# terminated or not. Option makes sense only when <tt>:terminator</tt>
-
# option is specified.
-
#
-
# * <tt>:scope</tt> - Indicates which methods should be executed when an
-
# object is used as a callback.
-
#
-
# class Audit
-
# def before(caller)
-
# puts 'Audit: before is called'
-
# end
-
#
-
# def before_save(caller)
-
# puts 'Audit: before_save is called'
-
# end
-
# end
-
#
-
# class Account
-
# include ActiveSupport::Callbacks
-
#
-
# define_callbacks :save
-
# set_callback :save, :before, Audit.new
-
#
-
# def save
-
# run_callbacks :save do
-
# puts 'save in main'
-
# end
-
# end
-
# end
-
#
-
# In the above case whenever you save an account the method
-
# <tt>Audit#before</tt> will be called. On the other hand
-
#
-
# define_callbacks :save, scope: [:kind, :name]
-
#
-
# would trigger <tt>Audit#before_save</tt> instead. That's constructed
-
# by calling <tt>#{kind}_#{name}</tt> on the given instance. In this
-
# case "kind" is "before" and "name" is "save". In this context +:kind+
-
# and +:name+ have special meanings: +:kind+ refers to the kind of
-
# callback (before/after/around) and +:name+ refers to the method on
-
# which callbacks are being defined.
-
#
-
# A declaration like
-
#
-
# define_callbacks :save, scope: [:name]
-
#
-
# would call <tt>Audit#save</tt>.
-
1
def define_callbacks(*callbacks)
-
3
config = callbacks.last.is_a?(Hash) ? callbacks.pop : {}
-
3
callbacks.each do |callback|
-
4
class_attribute "_#{callback}_callbacks"
-
4
send("_#{callback}_callbacks=", CallbackChain.new(callback, config))
-
end
-
end
-
end
-
end
-
end
-
1
module ActiveSupport
-
# A typical module looks like this:
-
#
-
# module M
-
# def self.included(base)
-
# base.extend ClassMethods
-
# scope :disabled, -> { where(disabled: true) }
-
# end
-
#
-
# module ClassMethods
-
# ...
-
# end
-
# end
-
#
-
# By using <tt>ActiveSupport::Concern</tt> the above module could instead be
-
# written as:
-
#
-
# require 'active_support/concern'
-
#
-
# module M
-
# extend ActiveSupport::Concern
-
#
-
# included do
-
# scope :disabled, -> { where(disabled: true) }
-
# end
-
#
-
# module ClassMethods
-
# ...
-
# end
-
# end
-
#
-
# Moreover, it gracefully handles module dependencies. Given a +Foo+ module
-
# and a +Bar+ module which depends on the former, we would typically write the
-
# following:
-
#
-
# module Foo
-
# def self.included(base)
-
# base.class_eval do
-
# def self.method_injected_by_foo
-
# ...
-
# end
-
# end
-
# end
-
# end
-
#
-
# module Bar
-
# def self.included(base)
-
# base.method_injected_by_foo
-
# end
-
# end
-
#
-
# class Host
-
# include Foo # We need to include this dependency for Bar
-
# include Bar # Bar is the module that Host really needs
-
# end
-
#
-
# But why should +Host+ care about +Bar+'s dependencies, namely +Foo+? We
-
# could try to hide these from +Host+ directly including +Foo+ in +Bar+:
-
#
-
# module Bar
-
# include Foo
-
# def self.included(base)
-
# base.method_injected_by_foo
-
# end
-
# end
-
#
-
# class Host
-
# include Bar
-
# end
-
#
-
# Unfortunately this won't work, since when +Foo+ is included, its <tt>base</tt>
-
# is the +Bar+ module, not the +Host+ class. With <tt>ActiveSupport::Concern</tt>,
-
# module dependencies are properly resolved:
-
#
-
# require 'active_support/concern'
-
#
-
# module Foo
-
# extend ActiveSupport::Concern
-
# included do
-
# class_eval do
-
# def self.method_injected_by_foo
-
# ...
-
# end
-
# end
-
# end
-
# end
-
#
-
# module Bar
-
# extend ActiveSupport::Concern
-
# include Foo
-
#
-
# included do
-
# self.method_injected_by_foo
-
# end
-
# end
-
#
-
# class Host
-
# include Bar # works, Bar takes care now of its dependencies
-
# end
-
1
module Concern
-
1
def self.extended(base) #:nodoc:
-
56
base.instance_variable_set("@_dependencies", [])
-
end
-
-
1
def append_features(base)
-
212
if base.instance_variable_defined?("@_dependencies")
-
47
base.instance_variable_get("@_dependencies") << self
-
47
return false
-
else
-
165
return false if base < self
-
206
@_dependencies.each { |dep| base.send(:include, dep) }
-
124
super
-
124
base.extend const_get("ClassMethods") if const_defined?("ClassMethods")
-
124
base.class_eval(&@_included_block) if instance_variable_defined?("@_included_block")
-
end
-
end
-
-
1
def included(base = nil, &block)
-
242
if base.nil?
-
30
@_included_block = block
-
else
-
212
super
-
end
-
end
-
end
-
end
-
1
require 'active_support/concern'
-
1
require 'active_support/ordered_options'
-
1
require 'active_support/core_ext/array/extract_options'
-
-
1
module ActiveSupport
-
# Configurable provides a <tt>config</tt> method to store and retrieve
-
# configuration options as an <tt>OrderedHash</tt>.
-
1
module Configurable
-
1
extend ActiveSupport::Concern
-
-
1
class Configuration < ActiveSupport::InheritableOptions
-
1
def compile_methods!
-
self.class.compile_methods!(keys)
-
end
-
-
# Compiles reader methods so we don't have to go through method_missing.
-
1
def self.compile_methods!(keys)
-
keys.reject { |m| method_defined?(m) }.each do |key|
-
class_eval <<-RUBY, __FILE__, __LINE__ + 1
-
def #{key}; _get(#{key.inspect}); end
-
RUBY
-
end
-
end
-
end
-
-
1
module ClassMethods
-
1
def config
-
@_config ||= if respond_to?(:superclass) && superclass.respond_to?(:config)
-
20
superclass.config.inheritable_copy
-
else
-
# create a new "anonymous" class that will host the compiled reader methods
-
1
Class.new(Configuration).new
-
387
end
-
end
-
-
1
def configure
-
5
yield config
-
end
-
-
# Allows you to add shortcut so that you don't have to refer to attribute
-
# through config. Also look at the example for config to contrast.
-
#
-
# Defines both class and instance config accessors.
-
#
-
# class User
-
# include ActiveSupport::Configurable
-
# config_accessor :allowed_access
-
# end
-
#
-
# User.allowed_access # => nil
-
# User.allowed_access = false
-
# User.allowed_access # => false
-
#
-
# user = User.new
-
# user.allowed_access # => false
-
# user.allowed_access = true
-
# user.allowed_access # => true
-
#
-
# User.allowed_access # => false
-
#
-
# The attribute name must be a valid method name in Ruby.
-
#
-
# class User
-
# include ActiveSupport::Configurable
-
# config_accessor :"1_Badname"
-
# end
-
# # => NameError: invalid config attribute name
-
#
-
# To opt out of the instance writer method, pass <tt>instance_writer: false</tt>.
-
# To opt out of the instance reader method, pass <tt>instance_reader: false</tt>.
-
#
-
# class User
-
# include ActiveSupport::Configurable
-
# config_accessor :allowed_access, instance_reader: false, instance_writer: false
-
# end
-
#
-
# User.allowed_access = false
-
# User.allowed_access # => false
-
#
-
# User.new.allowed_access = true # => NoMethodError
-
# User.new.allowed_access # => NoMethodError
-
#
-
# Or pass <tt>instance_accessor: false</tt>, to opt out both instance methods.
-
#
-
# class User
-
# include ActiveSupport::Configurable
-
# config_accessor :allowed_access, instance_accessor: false
-
# end
-
#
-
# User.allowed_access = false
-
# User.allowed_access # => false
-
#
-
# User.new.allowed_access = true # => NoMethodError
-
# User.new.allowed_access # => NoMethodError
-
#
-
# Also you can pass a block to set up the attribute with a default value.
-
#
-
# class User
-
# include ActiveSupport::Configurable
-
# config_accessor :hair_colors do
-
# [:brown, :black, :blonde, :red]
-
# end
-
# end
-
#
-
# User.hair_colors # => [:brown, :black, :blonde, :red]
-
1
def config_accessor(*names)
-
8
options = names.extract_options!
-
-
8
names.each do |name|
-
18
raise NameError.new('invalid config attribute name') unless name =~ /^[_A-Za-z]\w*$/
-
-
18
reader, reader_line = "def #{name}; config.#{name}; end", __LINE__
-
18
writer, writer_line = "def #{name}=(value); config.#{name} = value; end", __LINE__
-
-
18
singleton_class.class_eval reader, __FILE__, reader_line
-
18
singleton_class.class_eval writer, __FILE__, writer_line
-
-
18
unless options[:instance_accessor] == false
-
18
class_eval reader, __FILE__, reader_line unless options[:instance_reader] == false
-
18
class_eval writer, __FILE__, writer_line unless options[:instance_writer] == false
-
end
-
18
send("#{name}=", yield) if block_given?
-
end
-
end
-
end
-
-
# Reads and writes attributes from a configuration <tt>OrderedHash</tt>.
-
#
-
# require 'active_support/configurable'
-
#
-
# class User
-
# include ActiveSupport::Configurable
-
# end
-
#
-
# user = User.new
-
#
-
# user.config.allowed_access = true
-
# user.config.level = 1
-
#
-
# user.config.allowed_access # => true
-
# user.config.level # => 1
-
1
def config
-
137
@_config ||= self.class.config.inheritable_copy
-
end
-
end
-
end
-
-
1
class Array
-
# Returns the tail of the array from +position+.
-
#
-
# %w( a b c d ).from(0) # => ["a", "b", "c", "d"]
-
# %w( a b c d ).from(2) # => ["c", "d"]
-
# %w( a b c d ).from(10) # => []
-
# %w().from(0) # => []
-
1
def from(position)
-
self[position, length] || []
-
end
-
-
# Returns the beginning of the array up to +position+.
-
#
-
# %w( a b c d ).to(0) # => ["a"]
-
# %w( a b c d ).to(2) # => ["a", "b", "c"]
-
# %w( a b c d ).to(10) # => ["a", "b", "c", "d"]
-
# %w().to(0) # => []
-
1
def to(position)
-
first position + 1
-
end
-
-
# Equal to <tt>self[1]</tt>.
-
#
-
# %w( a b c d e).second # => "b"
-
1
def second
-
self[1]
-
end
-
-
# Equal to <tt>self[2]</tt>.
-
#
-
# %w( a b c d e).third # => "c"
-
1
def third
-
self[2]
-
end
-
-
# Equal to <tt>self[3]</tt>.
-
#
-
# %w( a b c d e).fourth # => "d"
-
1
def fourth
-
self[3]
-
end
-
-
# Equal to <tt>self[4]</tt>.
-
#
-
# %w( a b c d e).fifth # => "e"
-
1
def fifth
-
self[4]
-
end
-
-
# Equal to <tt>self[41]</tt>. Also known as accessing "the reddit".
-
1
def forty_two
-
self[41]
-
end
-
end
-
1
require 'active_support/xml_mini'
-
1
require 'active_support/core_ext/hash/keys'
-
1
require 'active_support/core_ext/string/inflections'
-
1
require 'active_support/core_ext/object/to_param'
-
1
require 'active_support/core_ext/object/to_query'
-
-
1
class Array
-
# Converts the array to a comma-separated sentence where the last element is
-
# joined by the connector word.
-
#
-
# You can pass the following options to change the default behaviour. If you
-
# pass an option key that doesn't exist in the list below, it will raise an
-
# <tt>ArgumentError</tt>.
-
#
-
# Options:
-
#
-
# * <tt>:words_connector</tt> - The sign or word used to join the elements
-
# in arrays with two or more elements (default: ", ").
-
# * <tt>:two_words_connector</tt> - The sign or word used to join the elements
-
# in arrays with two elements (default: " and ").
-
# * <tt>:last_word_connector</tt> - The sign or word used to join the last element
-
# in arrays with three or more elements (default: ", and ").
-
# * <tt>:locale</tt> - If +i18n+ is available, you can set a locale and use
-
# the connector options defined on the 'support.array' namespace in the
-
# corresponding dictionary file.
-
#
-
# [].to_sentence # => ""
-
# ['one'].to_sentence # => "one"
-
# ['one', 'two'].to_sentence # => "one and two"
-
# ['one', 'two', 'three'].to_sentence # => "one, two, and three"
-
#
-
# ['one', 'two'].to_sentence(passing: 'invalid option')
-
# # => ArgumentError: Unknown key :passing
-
#
-
# ['one', 'two'].to_sentence(two_words_connector: '-')
-
# # => "one-two"
-
#
-
# ['one', 'two', 'three'].to_sentence(words_connector: ' or ', last_word_connector: ' or at least ')
-
# # => "one or two or at least three"
-
#
-
# Examples using <tt>:locale</tt> option:
-
#
-
# # Given this locale dictionary:
-
# #
-
# # es:
-
# # support:
-
# # array:
-
# # words_connector: " o "
-
# # two_words_connector: " y "
-
# # last_word_connector: " o al menos "
-
#
-
# ['uno', 'dos'].to_sentence(locale: :es)
-
# # => "uno y dos"
-
#
-
# ['uno', 'dos', 'tres'].to_sentence(locale: :es)
-
# # => "uno o dos o al menos tres"
-
1
def to_sentence(options = {})
-
options.assert_valid_keys(:words_connector, :two_words_connector, :last_word_connector, :locale)
-
-
default_connectors = {
-
:words_connector => ', ',
-
:two_words_connector => ' and ',
-
:last_word_connector => ', and '
-
}
-
if defined?(I18n)
-
i18n_connectors = I18n.translate(:'support.array', locale: options[:locale], default: {})
-
default_connectors.merge!(i18n_connectors)
-
end
-
options = default_connectors.merge!(options)
-
-
case length
-
when 0
-
''
-
when 1
-
self[0].to_s.dup
-
when 2
-
"#{self[0]}#{options[:two_words_connector]}#{self[1]}"
-
else
-
"#{self[0...-1].join(options[:words_connector])}#{options[:last_word_connector]}#{self[-1]}"
-
end
-
end
-
-
# Converts a collection of elements into a formatted string by calling
-
# <tt>to_s</tt> on all elements and joining them. Having this model:
-
#
-
# class Blog < ActiveRecord::Base
-
# def to_s
-
# title
-
# end
-
# end
-
#
-
# Blog.all.map(&:title) #=> ["First Post", "Second Post", "Third post"]
-
#
-
# <tt>to_formatted_s</tt> shows us:
-
#
-
# Blog.all.to_formatted_s # => "First PostSecond PostThird Post"
-
#
-
# Adding in the <tt>:db</tt> argument as the format yields a comma separated
-
# id list:
-
#
-
# Blog.all.to_formatted_s(:db) # => "1,2,3"
-
1
def to_formatted_s(format = :default)
-
1
case format
-
when :db
-
if empty?
-
'null'
-
else
-
collect { |element| element.id }.join(',')
-
end
-
else
-
1
to_default_s
-
end
-
end
-
1
alias_method :to_default_s, :to_s
-
1
alias_method :to_s, :to_formatted_s
-
-
# Returns a string that represents the array in XML by invoking +to_xml+
-
# on each element. Active Record collections delegate their representation
-
# in XML to this method.
-
#
-
# All elements are expected to respond to +to_xml+, if any of them does
-
# not then an exception is raised.
-
#
-
# The root node reflects the class name of the first element in plural
-
# if all elements belong to the same type and that's not Hash:
-
#
-
# customer.projects.to_xml
-
#
-
# <?xml version="1.0" encoding="UTF-8"?>
-
# <projects type="array">
-
# <project>
-
# <amount type="decimal">20000.0</amount>
-
# <customer-id type="integer">1567</customer-id>
-
# <deal-date type="date">2008-04-09</deal-date>
-
# ...
-
# </project>
-
# <project>
-
# <amount type="decimal">57230.0</amount>
-
# <customer-id type="integer">1567</customer-id>
-
# <deal-date type="date">2008-04-15</deal-date>
-
# ...
-
# </project>
-
# </projects>
-
#
-
# Otherwise the root element is "objects":
-
#
-
# [{ foo: 1, bar: 2}, { baz: 3}].to_xml
-
#
-
# <?xml version="1.0" encoding="UTF-8"?>
-
# <objects type="array">
-
# <object>
-
# <bar type="integer">2</bar>
-
# <foo type="integer">1</foo>
-
# </object>
-
# <object>
-
# <baz type="integer">3</baz>
-
# </object>
-
# </objects>
-
#
-
# If the collection is empty the root element is "nil-classes" by default:
-
#
-
# [].to_xml
-
#
-
# <?xml version="1.0" encoding="UTF-8"?>
-
# <nil-classes type="array"/>
-
#
-
# To ensure a meaningful root element use the <tt>:root</tt> option:
-
#
-
# customer_with_no_projects.projects.to_xml(root: 'projects')
-
#
-
# <?xml version="1.0" encoding="UTF-8"?>
-
# <projects type="array"/>
-
#
-
# By default name of the node for the children of root is <tt>root.singularize</tt>.
-
# You can change it with the <tt>:children</tt> option.
-
#
-
# The +options+ hash is passed downwards:
-
#
-
# Message.all.to_xml(skip_types: true)
-
#
-
# <?xml version="1.0" encoding="UTF-8"?>
-
# <messages>
-
# <message>
-
# <created-at>2008-03-07T09:58:18+01:00</created-at>
-
# <id>1</id>
-
# <name>1</name>
-
# <updated-at>2008-03-07T09:58:18+01:00</updated-at>
-
# <user-id>1</user-id>
-
# </message>
-
# </messages>
-
#
-
1
def to_xml(options = {})
-
require 'active_support/builder' unless defined?(Builder)
-
-
options = options.dup
-
options[:indent] ||= 2
-
options[:builder] ||= Builder::XmlMarkup.new(indent: options[:indent])
-
options[:root] ||= \
-
if first.class != Hash && all? { |e| e.is_a?(first.class) }
-
underscored = ActiveSupport::Inflector.underscore(first.class.name)
-
ActiveSupport::Inflector.pluralize(underscored).tr('/', '_')
-
else
-
'objects'
-
end
-
-
builder = options[:builder]
-
builder.instruct! unless options.delete(:skip_instruct)
-
-
root = ActiveSupport::XmlMini.rename_key(options[:root].to_s, options)
-
children = options.delete(:children) || root.singularize
-
attributes = options[:skip_types] ? {} : { type: 'array' }
-
-
if empty?
-
builder.tag!(root, attributes)
-
else
-
builder.tag!(root, attributes) do
-
each { |value| ActiveSupport::XmlMini.to_tag(children, value, options) }
-
yield builder if block_given?
-
end
-
end
-
end
-
end
-
1
class Hash
-
# By default, only instances of Hash itself are extractable.
-
# Subclasses of Hash may implement this method and return
-
# true to declare themselves as extractable. If a Hash
-
# is extractable, Array#extract_options! pops it from
-
# the Array when it is the last element of the Array.
-
1
def extractable_options?
-
6
instance_of?(Hash)
-
end
-
end
-
-
1
class Array
-
# Extracts options from a set of arguments. Removes and returns the last
-
# element in the array if it's a hash, otherwise returns a blank hash.
-
#
-
# def options(*args)
-
# args.extract_options!
-
# end
-
#
-
# options(1, 2) # => {}
-
# options(1, 2, a: :b) # => {:a=>:b}
-
1
def extract_options!
-
142
if last.is_a?(Hash) && last.extractable_options?
-
6
pop
-
else
-
136
{}
-
end
-
end
-
end
-
1
class Array
-
# The human way of thinking about adding stuff to the end of a list is with append
-
1
alias_method :append, :<<
-
-
# The human way of thinking about adding stuff to the beginning of a list is with prepend
-
1
alias_method :prepend, :unshift
-
end
-
1
require 'benchmark'
-
-
1
class << Benchmark
-
1
def ms
-
2
1000 * realtime { yield }
-
end
-
end
-
1
require 'bigdecimal'
-
1
require 'yaml'
-
-
1
class BigDecimal
-
1
YAML_MAPPING = { 'Infinity' => '.Inf', '-Infinity' => '-.Inf', 'NaN' => '.NaN' }
-
-
1
def encode_with(coder)
-
string = to_s
-
coder.represent_scalar(nil, YAML_MAPPING[string] || string)
-
end
-
-
# Backport this method if it doesn't exist
-
1
unless method_defined?(:to_d)
-
1
def to_d
-
self
-
end
-
end
-
-
1
DEFAULT_STRING_FORMAT = 'F'
-
1
def to_formatted_s(*args)
-
if args[0].is_a?(Symbol)
-
super
-
else
-
format = args[0] || DEFAULT_STRING_FORMAT
-
_original_to_s(format)
-
end
-
end
-
1
alias_method :_original_to_s, :to_s
-
1
alias_method :to_s, :to_formatted_s
-
end
-
1
require 'active_support/core_ext/class/attribute'
-
1
require 'active_support/core_ext/class/attribute_accessors'
-
1
require 'active_support/core_ext/class/delegating_attributes'
-
1
require 'active_support/core_ext/class/subclasses'
-
1
require 'active_support/core_ext/kernel/singleton_class'
-
1
require 'active_support/core_ext/module/remove_method'
-
1
require 'active_support/core_ext/array/extract_options'
-
-
1
class Class
-
# Declare a class-level attribute whose value is inheritable by subclasses.
-
# Subclasses can change their own value and it will not impact parent class.
-
#
-
# class Base
-
# class_attribute :setting
-
# end
-
#
-
# class Subclass < Base
-
# end
-
#
-
# Base.setting = true
-
# Subclass.setting # => true
-
# Subclass.setting = false
-
# Subclass.setting # => false
-
# Base.setting # => true
-
#
-
# In the above case as long as Subclass does not assign a value to setting
-
# by performing <tt>Subclass.setting = _something_ </tt>, <tt>Subclass.setting</tt>
-
# would read value assigned to parent class. Once Subclass assigns a value then
-
# the value assigned by Subclass would be returned.
-
#
-
# This matches normal Ruby method inheritance: think of writing an attribute
-
# on a subclass as overriding the reader method. However, you need to be aware
-
# when using +class_attribute+ with mutable structures as +Array+ or +Hash+.
-
# In such cases, you don't want to do changes in places but use setters:
-
#
-
# Base.setting = []
-
# Base.setting # => []
-
# Subclass.setting # => []
-
#
-
# # Appending in child changes both parent and child because it is the same object:
-
# Subclass.setting << :foo
-
# Base.setting # => [:foo]
-
# Subclass.setting # => [:foo]
-
#
-
# # Use setters to not propagate changes:
-
# Base.setting = []
-
# Subclass.setting += [:foo]
-
# Base.setting # => []
-
# Subclass.setting # => [:foo]
-
#
-
# For convenience, a query method is defined as well:
-
#
-
# Subclass.setting? # => false
-
#
-
# Instances may overwrite the class value in the same way:
-
#
-
# Base.setting = true
-
# object = Base.new
-
# object.setting # => true
-
# object.setting = false
-
# object.setting # => false
-
# Base.setting # => true
-
#
-
# To opt out of the instance reader method, pass <tt>instance_reader: false</tt>.
-
#
-
# object.setting # => NoMethodError
-
# object.setting? # => NoMethodError
-
#
-
# To opt out of the instance writer method, pass <tt>instance_writer: false</tt>.
-
#
-
# object.setting = false # => NoMethodError
-
#
-
# To opt out of both instance methods, pass <tt>instance_accessor: false</tt>.
-
1
def class_attribute(*attrs)
-
59
options = attrs.extract_options!
-
59
instance_reader = options.fetch(:instance_accessor, true) && options.fetch(:instance_reader, true)
-
59
instance_writer = options.fetch(:instance_accessor, true) && options.fetch(:instance_writer, true)
-
-
59
attrs.each do |name|
-
64
class_eval <<-RUBY, __FILE__, __LINE__ + 1
-
def self.#{name}() nil end
-
def self.#{name}?() !!#{name} end
-
-
def self.#{name}=(val)
-
singleton_class.class_eval do
-
remove_possible_method(:#{name})
-
define_method(:#{name}) { val }
-
end
-
-
if singleton_class?
-
class_eval do
-
remove_possible_method(:#{name})
-
def #{name}
-
defined?(@#{name}) ? @#{name} : singleton_class.#{name}
-
end
-
end
-
end
-
val
-
end
-
-
if instance_reader
-
remove_possible_method :#{name}
-
def #{name}
-
defined?(@#{name}) ? @#{name} : self.class.#{name}
-
end
-
-
def #{name}?
-
!!#{name}
-
end
-
end
-
RUBY
-
-
64
attr_writer name if instance_writer
-
end
-
end
-
-
1
private
-
1
def singleton_class?
-
282
ancestors.first != self
-
end
-
end
-
1
require 'active_support/core_ext/array/extract_options'
-
-
# Extends the class object with class and instance accessors for class attributes,
-
# just like the native attr* accessors for instance attributes.
-
1
class Class
-
# Defines a class attribute if it's not defined and creates a reader method that
-
# returns the attribute value.
-
#
-
# class Person
-
# cattr_reader :hair_colors
-
# end
-
#
-
# Person.class_variable_set("@@hair_colors", [:brown, :black])
-
# Person.hair_colors # => [:brown, :black]
-
# Person.new.hair_colors # => [:brown, :black]
-
#
-
# The attribute name must be a valid method name in Ruby.
-
#
-
# class Person
-
# cattr_reader :"1_Badname "
-
# end
-
# # => NameError: invalid attribute name
-
#
-
# If you want to opt out the instance reader method, you can pass <tt>instance_reader: false</tt>
-
# or <tt>instance_accessor: false</tt>.
-
#
-
# class Person
-
# cattr_reader :hair_colors, instance_reader: false
-
# end
-
#
-
# Person.new.hair_colors # => NoMethodError
-
1
def cattr_reader(*syms)
-
14
options = syms.extract_options!
-
14
syms.each do |sym|
-
14
raise NameError.new('invalid attribute name') unless sym =~ /^[_A-Za-z]\w*$/
-
14
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
-
unless defined? @@#{sym}
-
@@#{sym} = nil
-
end
-
-
def self.#{sym}
-
@@#{sym}
-
end
-
EOS
-
-
14
unless options[:instance_reader] == false || options[:instance_accessor] == false
-
13
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
-
def #{sym}
-
@@#{sym}
-
end
-
EOS
-
end
-
end
-
end
-
-
# Defines a class attribute if it's not defined and creates a writer method to allow
-
# assignment to the attribute.
-
#
-
# class Person
-
# cattr_writer :hair_colors
-
# end
-
#
-
# Person.hair_colors = [:brown, :black]
-
# Person.class_variable_get("@@hair_colors") # => [:brown, :black]
-
# Person.new.hair_colors = [:blonde, :red]
-
# Person.class_variable_get("@@hair_colors") # => [:blonde, :red]
-
#
-
# The attribute name must be a valid method name in Ruby.
-
#
-
# class Person
-
# cattr_writer :"1_Badname "
-
# end
-
# # => NameError: invalid attribute name
-
#
-
# If you want to opt out the instance writer method, pass <tt>instance_writer: false</tt>
-
# or <tt>instance_accessor: false</tt>.
-
#
-
# class Person
-
# cattr_writer :hair_colors, instance_writer: false
-
# end
-
#
-
# Person.new.hair_colors = [:blonde, :red] # => NoMethodError
-
#
-
# Also, you can pass a block to set up the attribute with a default value.
-
#
-
# class Person
-
# cattr_writer :hair_colors do
-
# [:brown, :black, :blonde, :red]
-
# end
-
# end
-
#
-
# Person.class_variable_get("@@hair_colors") # => [:brown, :black, :blonde, :red]
-
1
def cattr_writer(*syms)
-
13
options = syms.extract_options!
-
13
syms.each do |sym|
-
13
raise NameError.new('invalid attribute name') unless sym =~ /^[_A-Za-z]\w*$/
-
13
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
-
unless defined? @@#{sym}
-
@@#{sym} = nil
-
end
-
-
def self.#{sym}=(obj)
-
@@#{sym} = obj
-
end
-
EOS
-
-
13
unless options[:instance_writer] == false || options[:instance_accessor] == false
-
12
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
-
def #{sym}=(obj)
-
@@#{sym} = obj
-
end
-
EOS
-
end
-
13
send("#{sym}=", yield) if block_given?
-
end
-
end
-
-
# Defines both class and instance accessors for class attributes.
-
#
-
# class Person
-
# cattr_accessor :hair_colors
-
# end
-
#
-
# Person.hair_colors = [:brown, :black, :blonde, :red]
-
# Person.hair_colors # => [:brown, :black, :blonde, :red]
-
# Person.new.hair_colors # => [:brown, :black, :blonde, :red]
-
#
-
# If a subclass changes the value then that would also change the value for
-
# parent class. Similarly if parent class changes the value then that would
-
# change the value of subclasses too.
-
#
-
# class Male < Person
-
# end
-
#
-
# Male.hair_colors << :blue
-
# Person.hair_colors # => [:brown, :black, :blonde, :red, :blue]
-
#
-
# To opt out of the instance writer method, pass <tt>instance_writer: false</tt>.
-
# To opt out of the instance reader method, pass <tt>instance_reader: false</tt>.
-
#
-
# class Person
-
# cattr_accessor :hair_colors, instance_writer: false, instance_reader: false
-
# end
-
#
-
# Person.new.hair_colors = [:brown] # => NoMethodError
-
# Person.new.hair_colors # => NoMethodError
-
#
-
# Or pass <tt>instance_accessor: false</tt>, to opt out both instance methods.
-
#
-
# class Person
-
# cattr_accessor :hair_colors, instance_accessor: false
-
# end
-
#
-
# Person.new.hair_colors = [:brown] # => NoMethodError
-
# Person.new.hair_colors # => NoMethodError
-
#
-
# Also you can pass a block to set up the attribute with a default value.
-
#
-
# class Person
-
# cattr_accessor :hair_colors do
-
# [:brown, :black, :blonde, :red]
-
# end
-
# end
-
#
-
# Person.class_variable_get("@@hair_colors") #=> [:brown, :black, :blonde, :red]
-
1
def cattr_accessor(*syms, &blk)
-
13
cattr_reader(*syms)
-
13
cattr_writer(*syms, &blk)
-
end
-
end
-
1
require 'active_support/core_ext/kernel/singleton_class'
-
1
require 'active_support/core_ext/module/remove_method'
-
-
1
class Class
-
1
def superclass_delegating_accessor(name, options = {})
-
# Create private _name and _name= methods that can still be used if the public
-
# methods are overridden. This allows
-
_superclass_delegating_accessor("_#{name}")
-
-
# Generate the public methods name, name=, and name?
-
# These methods dispatch to the private _name, and _name= methods, making them
-
# overridable
-
singleton_class.send(:define_method, name) { send("_#{name}") }
-
singleton_class.send(:define_method, "#{name}?") { !!send("_#{name}") }
-
singleton_class.send(:define_method, "#{name}=") { |value| send("_#{name}=", value) }
-
-
# If an instance_reader is needed, generate methods for name and name= on the
-
# class itself, so instances will be able to see them
-
define_method(name) { send("_#{name}") } if options[:instance_reader] != false
-
define_method("#{name}?") { !!send("#{name}") } if options[:instance_reader] != false
-
end
-
-
1
private
-
# Take the object being set and store it in a method. This gives us automatic
-
# inheritance behavior, without having to store the object in an instance
-
# variable and look up the superclass chain manually.
-
1
def _stash_object_in_method(object, method, instance_reader = true)
-
singleton_class.remove_possible_method(method)
-
singleton_class.send(:define_method, method) { object }
-
remove_possible_method(method)
-
define_method(method) { object } if instance_reader
-
end
-
-
1
def _superclass_delegating_accessor(name, options = {})
-
singleton_class.send(:define_method, "#{name}=") do |value|
-
_stash_object_in_method(value, name, options[:instance_reader] != false)
-
end
-
send("#{name}=", nil)
-
end
-
end
-
1
require 'active_support/core_ext/module/anonymous'
-
1
require 'active_support/core_ext/module/reachable'
-
-
1
class Class
-
1
begin
-
1
ObjectSpace.each_object(Class.new) {}
-
-
1
def descendants # :nodoc:
-
descendants = []
-
ObjectSpace.each_object(singleton_class) do |k|
-
descendants.unshift k unless k == self
-
end
-
descendants
-
end
-
rescue StandardError # JRuby
-
def descendants # :nodoc:
-
descendants = []
-
ObjectSpace.each_object(Class) do |k|
-
descendants.unshift k if k < self
-
end
-
descendants.uniq!
-
descendants
-
end
-
end
-
-
# Returns an array with the direct children of +self+.
-
#
-
# Integer.subclasses # => [Fixnum, Bignum]
-
#
-
# class Foo; end
-
# class Bar < Foo; end
-
# class Baz < Foo; end
-
#
-
# Foo.subclasses # => [Baz, Bar]
-
1
def subclasses
-
subclasses, chain = [], descendants
-
chain.each do |k|
-
subclasses << k unless chain.any? { |c| c > k }
-
end
-
subclasses
-
end
-
end
-
1
require 'active_support/core_ext/object/acts_like'
-
-
1
class Date
-
# Duck-types as a Date-like class. See Object#acts_like?.
-
1
def acts_like_date?
-
true
-
end
-
end
-
1
require 'date'
-
1
require 'active_support/duration'
-
1
require 'active_support/core_ext/object/acts_like'
-
1
require 'active_support/core_ext/date/zones'
-
1
require 'active_support/core_ext/time/zones'
-
1
require 'active_support/core_ext/date_and_time/calculations'
-
-
1
class Date
-
1
include DateAndTime::Calculations
-
-
1
@beginning_of_week_default = nil
-
-
1
class << self
-
1
attr_accessor :beginning_of_week_default
-
-
# Returns the week start (e.g. :monday) for the current request, if this has been set (via Date.beginning_of_week=).
-
# If <tt>Date.beginning_of_week</tt> has not been set for the current request, returns the week start specified in <tt>config.beginning_of_week</tt>.
-
# If no config.beginning_of_week was specified, returns :monday.
-
1
def beginning_of_week
-
Thread.current[:beginning_of_week] || beginning_of_week_default || :monday
-
end
-
-
# Sets <tt>Date.beginning_of_week</tt> to a week start (e.g. :monday) for current request/thread.
-
#
-
# This method accepts any of the following day symbols:
-
# :monday, :tuesday, :wednesday, :thursday, :friday, :saturday, :sunday
-
1
def beginning_of_week=(week_start)
-
Thread.current[:beginning_of_week] = find_beginning_of_week!(week_start)
-
end
-
-
# Returns week start day symbol (e.g. :monday), or raises an ArgumentError for invalid day symbol.
-
1
def find_beginning_of_week!(week_start)
-
raise ArgumentError, "Invalid beginning of week: #{week_start}" unless ::Date::DAYS_INTO_WEEK.key?(week_start)
-
week_start
-
end
-
-
# Returns a new Date representing the date 1 day ago (i.e. yesterday's date).
-
1
def yesterday
-
::Date.current.yesterday
-
end
-
-
# Returns a new Date representing the date 1 day after today (i.e. tomorrow's date).
-
1
def tomorrow
-
::Date.current.tomorrow
-
end
-
-
# Returns Time.zone.today when <tt>Time.zone</tt> or <tt>config.time_zone</tt> are set, otherwise just returns Date.today.
-
1
def current
-
::Time.zone ? ::Time.zone.today : ::Date.today
-
end
-
end
-
-
# Converts Date to a Time (or DateTime if necessary) with the time portion set to the beginning of the day (0:00)
-
# and then subtracts the specified number of seconds.
-
1
def ago(seconds)
-
to_time_in_current_zone.since(-seconds)
-
end
-
-
# Converts Date to a Time (or DateTime if necessary) with the time portion set to the beginning of the day (0:00)
-
# and then adds the specified number of seconds
-
1
def since(seconds)
-
to_time_in_current_zone.since(seconds)
-
end
-
1
alias :in :since
-
-
# Converts Date to a Time (or DateTime if necessary) with the time portion set to the beginning of the day (0:00)
-
1
def beginning_of_day
-
to_time_in_current_zone
-
end
-
1
alias :midnight :beginning_of_day
-
1
alias :at_midnight :beginning_of_day
-
1
alias :at_beginning_of_day :beginning_of_day
-
-
# Converts Date to a Time (or DateTime if necessary) with the time portion set to the end of the day (23:59:59)
-
1
def end_of_day
-
to_time_in_current_zone.end_of_day
-
end
-
-
1
def plus_with_duration(other) #:nodoc:
-
1
if ActiveSupport::Duration === other
-
other.since(self)
-
else
-
1
plus_without_duration(other)
-
end
-
end
-
1
alias_method :plus_without_duration, :+
-
1
alias_method :+, :plus_with_duration
-
-
1
def minus_with_duration(other) #:nodoc:
-
if ActiveSupport::Duration === other
-
plus_with_duration(-other)
-
else
-
minus_without_duration(other)
-
end
-
end
-
1
alias_method :minus_without_duration, :-
-
1
alias_method :-, :minus_with_duration
-
-
# Provides precise Date calculations for years, months, and days. The +options+ parameter takes a hash with
-
# any of these keys: <tt>:years</tt>, <tt>:months</tt>, <tt>:weeks</tt>, <tt>:days</tt>.
-
1
def advance(options)
-
1
options = options.dup
-
1
d = self
-
1
d = d >> options.delete(:years) * 12 if options[:years]
-
1
d = d >> options.delete(:months) if options[:months]
-
1
d = d + options.delete(:weeks) * 7 if options[:weeks]
-
1
d = d + options.delete(:days) if options[:days]
-
1
d
-
end
-
-
# Returns a new Date where one or more of the elements have been changed according to the +options+ parameter.
-
# The +options+ parameter is a hash with a combination of these keys: <tt>:year</tt>, <tt>:month</tt>, <tt>:day</tt>.
-
#
-
# Date.new(2007, 5, 12).change(day: 1) # => Date.new(2007, 5, 1)
-
# Date.new(2007, 5, 12).change(year: 2005, month: 1) # => Date.new(2005, 1, 12)
-
1
def change(options)
-
::Date.new(
-
options.fetch(:year, year),
-
options.fetch(:month, month),
-
options.fetch(:day, day)
-
)
-
end
-
end
-
1
require 'date'
-
1
require 'active_support/inflector/methods'
-
1
require 'active_support/core_ext/date/zones'
-
1
require 'active_support/core_ext/module/remove_method'
-
-
1
class Date
-
1
DATE_FORMATS = {
-
:short => '%e %b',
-
:long => '%B %e, %Y',
-
:db => '%Y-%m-%d',
-
:number => '%Y%m%d',
-
:long_ordinal => lambda { |date|
-
day_format = ActiveSupport::Inflector.ordinalize(date.day)
-
date.strftime("%B #{day_format}, %Y") # => "April 25th, 2007"
-
},
-
:rfc822 => '%e %b %Y'
-
}
-
-
# Ruby 1.9 has Date#to_time which converts to localtime only.
-
1
remove_possible_method :to_time
-
-
# Ruby 1.9 has Date#xmlschema which converts to a string without the time component.
-
1
remove_possible_method :xmlschema
-
-
# Convert to a formatted string. See DATE_FORMATS for predefined formats.
-
#
-
# This method is aliased to <tt>to_s</tt>.
-
#
-
# date = Date.new(2007, 11, 10) # => Sat, 10 Nov 2007
-
#
-
# date.to_formatted_s(:db) # => "2007-11-10"
-
# date.to_s(:db) # => "2007-11-10"
-
#
-
# date.to_formatted_s(:short) # => "10 Nov"
-
# date.to_formatted_s(:long) # => "November 10, 2007"
-
# date.to_formatted_s(:long_ordinal) # => "November 10th, 2007"
-
# date.to_formatted_s(:rfc822) # => "10 Nov 2007"
-
#
-
# == Adding your own time formats to to_formatted_s
-
# You can add your own formats to the Date::DATE_FORMATS hash.
-
# Use the format name as the hash key and either a strftime string
-
# or Proc instance that takes a date argument as the value.
-
#
-
# # config/initializers/time_formats.rb
-
# Date::DATE_FORMATS[:month_and_year] = '%B %Y'
-
# Date::DATE_FORMATS[:short_ordinal] = ->(date) { date.strftime("%B #{date.day.ordinalize}") }
-
1
def to_formatted_s(format = :default)
-
if formatter = DATE_FORMATS[format]
-
if formatter.respond_to?(:call)
-
formatter.call(self).to_s
-
else
-
strftime(formatter)
-
end
-
else
-
to_default_s
-
end
-
end
-
1
alias_method :to_default_s, :to_s
-
1
alias_method :to_s, :to_formatted_s
-
-
# Overrides the default inspect method with a human readable one, e.g., "Mon, 21 Feb 2005"
-
1
def readable_inspect
-
strftime('%a, %d %b %Y')
-
end
-
1
alias_method :default_inspect, :inspect
-
1
alias_method :inspect, :readable_inspect
-
-
# Converts a Date instance to a Time, where the time is set to the beginning of the day.
-
# The timezone can be either :local or :utc (default :local).
-
#
-
# date = Date.new(2007, 11, 10) # => Sat, 10 Nov 2007
-
#
-
# date.to_time # => Sat Nov 10 00:00:00 0800 2007
-
# date.to_time(:local) # => Sat Nov 10 00:00:00 0800 2007
-
#
-
# date.to_time(:utc) # => Sat Nov 10 00:00:00 UTC 2007
-
1
def to_time(form = :local)
-
::Time.send("#{form}_time", year, month, day)
-
end
-
-
1
def xmlschema
-
to_time_in_current_zone.xmlschema
-
end
-
end
-
1
require 'date'
-
1
require 'active_support/core_ext/time/zones'
-
-
1
class Date
-
# Converts Date to a TimeWithZone in the current zone if <tt>Time.zone</tt> or
-
# <tt>Time.zone_default</tt> is set, otherwise converts Date to a Time via
-
# Date#to_time.
-
1
def to_time_in_current_zone
-
if ::Time.zone
-
::Time.zone.local(year, month, day)
-
else
-
to_time
-
end
-
end
-
end
-
1
module DateAndTime
-
1
module Calculations
-
1
DAYS_INTO_WEEK = {
-
:monday => 0,
-
:tuesday => 1,
-
:wednesday => 2,
-
:thursday => 3,
-
:friday => 4,
-
:saturday => 5,
-
:sunday => 6
-
}
-
-
# Returns a new date/time representing yesterday.
-
1
def yesterday
-
advance(:days => -1)
-
end
-
-
# Returns a new date/time representing tomorrow.
-
1
def tomorrow
-
advance(:days => 1)
-
end
-
-
# Returns true if the date/time is today.
-
1
def today?
-
to_date == ::Date.current
-
end
-
-
# Returns true if the date/time is in the past.
-
1
def past?
-
self < self.class.current
-
end
-
-
# Returns true if the date/time is in the future.
-
1
def future?
-
self > self.class.current
-
end
-
-
# Returns a new date/time the specified number of days ago.
-
1
def days_ago(days)
-
advance(:days => -days)
-
end
-
-
# Returns a new date/time the specified number of days in the future.
-
1
def days_since(days)
-
advance(:days => days)
-
end
-
-
# Returns a new date/time the specified number of weeks ago.
-
1
def weeks_ago(weeks)
-
advance(:weeks => -weeks)
-
end
-
-
# Returns a new date/time the specified number of weeks in the future.
-
1
def weeks_since(weeks)
-
advance(:weeks => weeks)
-
end
-
-
# Returns a new date/time the specified number of months ago.
-
1
def months_ago(months)
-
advance(:months => -months)
-
end
-
-
# Returns a new date/time the specified number of months in the future.
-
1
def months_since(months)
-
advance(:months => months)
-
end
-
-
# Returns a new date/time the specified number of years ago.
-
1
def years_ago(years)
-
advance(:years => -years)
-
end
-
-
# Returns a new date/time the specified number of years in the future.
-
1
def years_since(years)
-
advance(:years => years)
-
end
-
-
# Returns a new date/time at the start of the month.
-
# DateTime objects will have a time set to 0:00.
-
1
def beginning_of_month
-
first_hour{ change(:day => 1) }
-
end
-
1
alias :at_beginning_of_month :beginning_of_month
-
-
# Returns a new date/time at the start of the quarter.
-
# Example: 1st January, 1st July, 1st October.
-
# DateTime objects will have a time set to 0:00.
-
1
def beginning_of_quarter
-
first_quarter_month = [10, 7, 4, 1].detect { |m| m <= month }
-
beginning_of_month.change(:month => first_quarter_month)
-
end
-
1
alias :at_beginning_of_quarter :beginning_of_quarter
-
-
# Returns a new date/time at the end of the quarter.
-
# Example: 31st March, 30th June, 30th September.
-
# DateTIme objects will have a time set to 23:59:59.
-
1
def end_of_quarter
-
last_quarter_month = [3, 6, 9, 12].detect { |m| m >= month }
-
beginning_of_month.change(:month => last_quarter_month).end_of_month
-
end
-
1
alias :at_end_of_quarter :end_of_quarter
-
-
# Return a new date/time at the beginning of the year.
-
# Example: 1st January.
-
# DateTime objects will have a time set to 0:00.
-
1
def beginning_of_year
-
change(:month => 1).beginning_of_month
-
end
-
1
alias :at_beginning_of_year :beginning_of_year
-
-
# Returns a new date/time representing the given day in the next week.
-
# Week is assumed to start on +start_day+, default is
-
# +Date.beginning_of_week+ or +config.beginning_of_week+ when set.
-
# DateTime objects have their time set to 0:00.
-
1
def next_week(start_day = Date.beginning_of_week)
-
first_hour{ weeks_since(1).beginning_of_week.days_since(days_span(start_day)) }
-
end
-
-
# Short-hand for months_since(1).
-
1
def next_month
-
months_since(1)
-
end
-
-
# Short-hand for months_since(3)
-
1
def next_quarter
-
months_since(3)
-
end
-
-
# Short-hand for years_since(1).
-
1
def next_year
-
years_since(1)
-
end
-
-
# Returns a new date/time representing the given day in the previous week.
-
# Week is assumed to start on +start_day+, default is
-
# +Date.beginning_of_week+ or +config.beginning_of_week+ when set.
-
# DateTime objects have their time set to 0:00.
-
1
def prev_week(start_day = Date.beginning_of_week)
-
first_hour{ weeks_ago(1).beginning_of_week.days_since(days_span(start_day)) }
-
end
-
1
alias_method :last_week, :prev_week
-
-
# Short-hand for months_ago(1).
-
1
def prev_month
-
months_ago(1)
-
end
-
1
alias_method :last_month, :prev_month
-
-
# Short-hand for months_ago(3).
-
1
def prev_quarter
-
months_ago(3)
-
end
-
1
alias_method :last_quarter, :prev_quarter
-
-
# Short-hand for years_ago(1).
-
1
def prev_year
-
years_ago(1)
-
end
-
1
alias_method :last_year, :prev_year
-
-
# Returns the number of days to the start of the week on the given day.
-
# Week is assumed to start on +start_day+, default is
-
# +Date.beginning_of_week+ or +config.beginning_of_week+ when set.
-
1
def days_to_week_start(start_day = Date.beginning_of_week)
-
start_day_number = DAYS_INTO_WEEK[start_day]
-
current_day_number = wday != 0 ? wday - 1 : 6
-
(current_day_number - start_day_number) % 7
-
end
-
-
# Returns a new date/time representing the start of this week on the given day.
-
# Week is assumed to start on +start_day+, default is
-
# +Date.beginning_of_week+ or +config.beginning_of_week+ when set.
-
# +DateTime+ objects have their time set to 0:00.
-
1
def beginning_of_week(start_day = Date.beginning_of_week)
-
result = days_ago(days_to_week_start(start_day))
-
acts_like?(:time) ? result.midnight : result
-
end
-
1
alias :at_beginning_of_week :beginning_of_week
-
-
# Returns Monday of this week assuming that week starts on Monday.
-
# +DateTime+ objects have their time set to 0:00.
-
1
def monday
-
beginning_of_week(:monday)
-
end
-
-
# Returns a new date/time representing the end of this week on the given day.
-
# Week is assumed to start on +start_day+, default is
-
# +Date.beginning_of_week+ or +config.beginning_of_week+ when set.
-
# DateTime objects have their time set to 23:59:59.
-
1
def end_of_week(start_day = Date.beginning_of_week)
-
last_hour{ days_since(6 - days_to_week_start(start_day)) }
-
end
-
1
alias :at_end_of_week :end_of_week
-
-
# Returns Sunday of this week assuming that week starts on Monday.
-
# +DateTime+ objects have their time set to 23:59:59.
-
1
def sunday
-
end_of_week(:monday)
-
end
-
-
# Returns a new date/time representing the end of the month.
-
# DateTime objects will have a time set to 23:59:59.
-
1
def end_of_month
-
last_day = ::Time.days_in_month(month, year)
-
last_hour{ days_since(last_day - day) }
-
end
-
1
alias :at_end_of_month :end_of_month
-
-
# Returns a new date/time representing the end of the year.
-
# DateTime objects will have a time set to 23:59:59.
-
1
def end_of_year
-
change(:month => 12).end_of_month
-
end
-
1
alias :at_end_of_year :end_of_year
-
-
1
private
-
-
1
def first_hour
-
result = yield
-
acts_like?(:time) ? result.change(:hour => 0) : result
-
end
-
-
1
def last_hour
-
result = yield
-
acts_like?(:time) ? result.end_of_day : result
-
end
-
-
1
def days_span(day)
-
(DAYS_INTO_WEEK[day] - DAYS_INTO_WEEK[Date.beginning_of_week]) % 7
-
end
-
end
-
end
-
1
require 'active_support/core_ext/object/acts_like'
-
-
1
class DateTime
-
# Duck-types as a Date-like class. See Object#acts_like?.
-
1
def acts_like_date?
-
true
-
end
-
-
# Duck-types as a Time-like class. See Object#acts_like?.
-
1
def acts_like_time?
-
true
-
end
-
end
-
1
require 'active_support/deprecation'
-
-
1
class DateTime
-
1
class << self
-
# *DEPRECATED*: Use +DateTime.civil_from_format+ directly.
-
1
def local_offset
-
ActiveSupport::Deprecation.warn 'DateTime.local_offset is deprecated. Use DateTime.civil_from_format directly.'
-
-
::Time.local(2012).utc_offset.to_r / 86400
-
end
-
-
# Returns <tt>Time.zone.now.to_datetime</tt> when <tt>Time.zone</tt> or
-
# <tt>config.time_zone</tt> are set, otherwise returns
-
# <tt>Time.now.to_datetime</tt>.
-
1
def current
-
::Time.zone ? ::Time.zone.now.to_datetime : ::Time.now.to_datetime
-
end
-
end
-
-
# Tells whether the DateTime object's datetime lies in the past.
-
1
def past?
-
self < ::DateTime.current
-
end
-
-
# Tells whether the DateTime object's datetime lies in the future.
-
1
def future?
-
self > ::DateTime.current
-
end
-
-
# Seconds since midnight: DateTime.now.seconds_since_midnight.
-
1
def seconds_since_midnight
-
sec + (min * 60) + (hour * 3600)
-
end
-
-
# Returns a new DateTime where one or more of the elements have been changed
-
# according to the +options+ parameter. The time options (<tt>:hour</tt>,
-
# <tt>:minute</tt>, <tt>:sec</tt>) reset cascadingly, so if only the hour is
-
# passed, then minute and sec is set to 0. If the hour and minute is passed,
-
# then sec is set to 0. The +options+ parameter takes a hash with any of these
-
# keys: <tt>:year</tt>, <tt>:month</tt>, <tt>:day</tt>, <tt>:hour</tt>,
-
# <tt>:min</tt>, <tt>:sec</tt>, <tt>:offset</tt>, <tt>:start</tt>.
-
#
-
# DateTime.new(2012, 8, 29, 22, 35, 0).change(day: 1) # => DateTime.new(2012, 8, 1, 22, 35, 0)
-
# DateTime.new(2012, 8, 29, 22, 35, 0).change(year: 1981, day: 1) # => DateTime.new(1981, 8, 1, 22, 35, 0)
-
# DateTime.new(2012, 8, 29, 22, 35, 0).change(year: 1981, hour: 0) # => DateTime.new(1981, 8, 29, 0, 0, 0)
-
1
def change(options)
-
::DateTime.civil(
-
options.fetch(:year, year),
-
options.fetch(:month, month),
-
options.fetch(:day, day),
-
options.fetch(:hour, hour),
-
options.fetch(:min, options[:hour] ? 0 : min),
-
options.fetch(:sec, (options[:hour] || options[:min]) ? 0 : sec),
-
options.fetch(:offset, offset),
-
options.fetch(:start, start)
-
)
-
end
-
-
# Uses Date to provide precise Time calculations for years, months, and days.
-
# The +options+ parameter takes a hash with any of these keys: <tt>:years</tt>,
-
# <tt>:months</tt>, <tt>:weeks</tt>, <tt>:days</tt>, <tt>:hours</tt>,
-
# <tt>:minutes</tt>, <tt>:seconds</tt>.
-
1
def advance(options)
-
d = to_date.advance(options)
-
datetime_advanced_by_date = change(:year => d.year, :month => d.month, :day => d.day)
-
seconds_to_advance = \
-
options.fetch(:seconds, 0) +
-
options.fetch(:minutes, 0) * 60 +
-
options.fetch(:hours, 0) * 3600
-
-
if seconds_to_advance.zero?
-
datetime_advanced_by_date
-
else
-
datetime_advanced_by_date.since seconds_to_advance
-
end
-
end
-
-
# Returns a new DateTime representing the time a number of seconds ago.
-
# Do not use this method in combination with x.months, use months_ago instead!
-
1
def ago(seconds)
-
since(-seconds)
-
end
-
-
# Returns a new DateTime representing the time a number of seconds since the
-
# instance time. Do not use this method in combination with x.months, use
-
# months_since instead!
-
1
def since(seconds)
-
self + Rational(seconds.round, 86400)
-
end
-
1
alias :in :since
-
-
# Returns a new DateTime representing the start of the day (0:00).
-
1
def beginning_of_day
-
change(:hour => 0)
-
end
-
1
alias :midnight :beginning_of_day
-
1
alias :at_midnight :beginning_of_day
-
1
alias :at_beginning_of_day :beginning_of_day
-
-
# Returns a new DateTime representing the end of the day (23:59:59).
-
1
def end_of_day
-
change(:hour => 23, :min => 59, :sec => 59)
-
end
-
-
# Returns a new DateTime representing the start of the hour (hh:00:00).
-
1
def beginning_of_hour
-
change(:min => 0)
-
end
-
1
alias :at_beginning_of_hour :beginning_of_hour
-
-
# Returns a new DateTime representing the end of the hour (hh:59:59).
-
1
def end_of_hour
-
change(:min => 59, :sec => 59)
-
end
-
-
# Adjusts DateTime to UTC by adding its offset value; offset is set to 0.
-
#
-
# DateTime.civil(2005, 2, 21, 10, 11, 12, Rational(-6, 24)) # => Mon, 21 Feb 2005 10:11:12 -0600
-
# DateTime.civil(2005, 2, 21, 10, 11, 12, Rational(-6, 24)).utc # => Mon, 21 Feb 2005 16:11:12 +0000
-
1
def utc
-
new_offset(0)
-
end
-
1
alias_method :getutc, :utc
-
-
# Returns +true+ if <tt>offset == 0</tt>.
-
1
def utc?
-
offset == 0
-
end
-
-
# Returns the offset value in seconds.
-
1
def utc_offset
-
(offset * 86400).to_i
-
end
-
-
# Layers additional behavior on DateTime#<=> so that Time and
-
# ActiveSupport::TimeWithZone instances can be compared with a DateTime.
-
1
def <=>(other)
-
1
super other.to_datetime
-
end
-
-
end
-
1
require 'active_support/inflector/methods'
-
1
require 'active_support/core_ext/time/conversions'
-
1
require 'active_support/core_ext/date_time/calculations'
-
1
require 'active_support/values/time_zone'
-
-
1
class DateTime
-
# Convert to a formatted string. See Time::DATE_FORMATS for predefined formats.
-
#
-
# This method is aliased to <tt>to_s</tt>.
-
#
-
# === Examples
-
# datetime = DateTime.civil(2007, 12, 4, 0, 0, 0, 0) # => Tue, 04 Dec 2007 00:00:00 +0000
-
#
-
# datetime.to_formatted_s(:db) # => "2007-12-04 00:00:00"
-
# datetime.to_s(:db) # => "2007-12-04 00:00:00"
-
# datetime.to_s(:number) # => "20071204000000"
-
# datetime.to_formatted_s(:short) # => "04 Dec 00:00"
-
# datetime.to_formatted_s(:long) # => "December 04, 2007 00:00"
-
# datetime.to_formatted_s(:long_ordinal) # => "December 4th, 2007 00:00"
-
# datetime.to_formatted_s(:rfc822) # => "Tue, 04 Dec 2007 00:00:00 +0000"
-
#
-
# == Adding your own datetime formats to to_formatted_s
-
# DateTime formats are shared with Time. You can add your own to the
-
# Time::DATE_FORMATS hash. Use the format name as the hash key and
-
# either a strftime string or Proc instance that takes a time or
-
# datetime argument as the value.
-
#
-
# # config/initializers/time_formats.rb
-
# Time::DATE_FORMATS[:month_and_year] = '%B %Y'
-
# Time::DATE_FORMATS[:short_ordinal] = lambda { |time| time.strftime("%B #{time.day.ordinalize}") }
-
1
def to_formatted_s(format = :default)
-
1
if formatter = ::Time::DATE_FORMATS[format]
-
formatter.respond_to?(:call) ? formatter.call(self).to_s : strftime(formatter)
-
else
-
1
to_default_s
-
end
-
end
-
1
alias_method :to_default_s, :to_s if instance_methods(false).include?(:to_s)
-
1
alias_method :to_s, :to_formatted_s
-
-
#
-
# datetime = DateTime.civil(2000, 1, 1, 0, 0, 0, Rational(-6, 24))
-
# datetime.formatted_offset # => "-06:00"
-
# datetime.formatted_offset(false) # => "-0600"
-
1
def formatted_offset(colon = true, alternate_utc_string = nil)
-
utc? && alternate_utc_string || ActiveSupport::TimeZone.seconds_to_utc_offset(utc_offset, colon)
-
end
-
-
# Overrides the default inspect method with a human readable one, e.g., "Mon, 21 Feb 2005 14:30:00 +0000".
-
1
def readable_inspect
-
to_s(:rfc822)
-
end
-
1
alias_method :default_inspect, :inspect
-
1
alias_method :inspect, :readable_inspect
-
-
# Returns DateTime with local offset for given year if format is local else
-
# offset is zero.
-
#
-
# DateTime.civil_from_format :local, 2012
-
# # => Sun, 01 Jan 2012 00:00:00 +0300
-
# DateTime.civil_from_format :local, 2012, 12, 17
-
# # => Mon, 17 Dec 2012 00:00:00 +0000
-
1
def self.civil_from_format(utc_or_local, year, month=1, day=1, hour=0, min=0, sec=0)
-
if utc_or_local.to_sym == :local
-
offset = ::Time.local(year, month, day).utc_offset.to_r / 86400
-
else
-
offset = 0
-
end
-
civil(year, month, day, hour, min, sec, offset)
-
end
-
-
# Converts +self+ to a floating-point number of seconds since the Unix epoch.
-
1
def to_f
-
seconds_since_unix_epoch.to_f
-
end
-
-
# Converts +self+ to an integer number of seconds since the Unix epoch.
-
1
def to_i
-
seconds_since_unix_epoch.to_i
-
end
-
-
1
private
-
-
1
def offset_in_seconds
-
(offset * 86400).to_i
-
end
-
-
1
def seconds_since_unix_epoch
-
(jd - 2440588) * 86400 - offset_in_seconds + seconds_since_midnight
-
end
-
end
-
1
require 'active_support/core_ext/time/zones'
-
-
1
class DateTime
-
# Returns the simultaneous time in <tt>Time.zone</tt>.
-
#
-
# Time.zone = 'Hawaii' # => 'Hawaii'
-
# DateTime.new(2000).in_time_zone # => Fri, 31 Dec 1999 14:00:00 HST -10:00
-
#
-
# This method is similar to Time#localtime, except that it uses <tt>Time.zone</tt>
-
# as the local zone instead of the operating system's time zone.
-
#
-
# You can also pass in a TimeZone instance or string that identifies a TimeZone
-
# as an argument, and the conversion will be based on that zone instead of
-
# <tt>Time.zone</tt>.
-
#
-
# DateTime.new(2000).in_time_zone('Alaska') # => Fri, 31 Dec 1999 15:00:00 AKST -09:00
-
1
def in_time_zone(zone = ::Time.zone)
-
if zone
-
ActiveSupport::TimeWithZone.new(utc? ? self : getutc, ::Time.find_zone!(zone))
-
else
-
self
-
end
-
end
-
end
-
1
module Enumerable
-
# Calculates a sum from the elements.
-
#
-
# payments.sum { |p| p.price * p.tax_rate }
-
# payments.sum(&:price)
-
#
-
# The latter is a shortcut for:
-
#
-
# payments.inject(0) { |sum, p| sum + p.price }
-
#
-
# It can also calculate the sum without the use of a block.
-
#
-
# [5, 15, 10].sum # => 30
-
# ['foo', 'bar'].sum # => "foobar"
-
# [[1, 2], [3, 1, 5]].sum => [1, 2, 3, 1, 5]
-
#
-
# The default sum of an empty list is zero. You can override this default:
-
#
-
# [].sum(Payment.new(0)) { |i| i.amount } # => Payment.new(0)
-
1
def sum(identity = 0, &block)
-
if block_given?
-
map(&block).sum(identity)
-
else
-
inject { |sum, element| sum + element } || identity
-
end
-
end
-
-
# Convert an enumerable to a hash.
-
#
-
# people.index_by(&:login)
-
# => { "nextangle" => <Person ...>, "chade-" => <Person ...>, ...}
-
# people.index_by { |person| "#{person.first_name} #{person.last_name}" }
-
# => { "Chade- Fowlersburg-e" => <Person ...>, "David Heinemeier Hansson" => <Person ...>, ...}
-
1
def index_by
-
if block_given?
-
Hash[map { |elem| [yield(elem), elem] }]
-
else
-
to_enum :index_by
-
end
-
end
-
-
# Returns +true+ if the enumerable has more than 1 element. Functionally
-
# equivalent to <tt>enum.to_a.size > 1</tt>. Can be called with a block too,
-
# much like any?, so <tt>people.many? { |p| p.age > 26 }</tt> returns +true+
-
# if more than one person is over 26.
-
1
def many?
-
cnt = 0
-
if block_given?
-
any? do |element|
-
cnt += 1 if yield element
-
cnt > 1
-
end
-
else
-
any? { (cnt += 1) > 1 }
-
end
-
end
-
-
# The negative of the <tt>Enumerable#include?</tt>. Returns +true+ if the
-
# collection does not include the object.
-
1
def exclude?(object)
-
2
!include?(object)
-
end
-
end
-
-
1
class Range #:nodoc:
-
# Optimize range sum to use arithmetic progression if a block is not given and
-
# we have a range of numeric values.
-
1
def sum(identity = 0)
-
if block_given? || !(first.is_a?(Integer) && last.is_a?(Integer))
-
super
-
else
-
actual_last = exclude_end? ? (last - 1) : last
-
if actual_last >= first
-
(actual_last - first + 1) * (actual_last + first) / 2
-
else
-
identity
-
end
-
end
-
end
-
end
-
1
class Hash
-
# Returns a new hash with +self+ and +other_hash+ merged recursively.
-
#
-
# h1 = { x: { y: [4,5,6] }, z: [7,8,9] }
-
# h2 = { x: { y: [7,8,9] }, z: 'xyz' }
-
#
-
# h1.deep_merge(h2) #=> {x: {y: [7, 8, 9]}, z: "xyz"}
-
# h2.deep_merge(h1) #=> {x: {y: [4, 5, 6]}, z: [7, 8, 9]}
-
# h1.deep_merge(h2) { |key, old, new| Array.wrap(old) + Array.wrap(new) }
-
# #=> {:x=>{:y=>[4, 5, 6, 7, 8, 9]}, :z=>[7, 8, 9, "xyz"]}
-
1
def deep_merge(other_hash, &block)
-
1
dup.deep_merge!(other_hash, &block)
-
end
-
-
# Same as +deep_merge+, but modifies +self+.
-
1
def deep_merge!(other_hash, &block)
-
6
other_hash.each_pair do |k,v|
-
10
tv = self[k]
-
10
if tv.is_a?(Hash) && v.is_a?(Hash)
-
1
self[k] = tv.deep_merge(v, &block)
-
else
-
9
self[k] = block && tv ? block.call(k, tv, v) : v
-
end
-
end
-
6
self
-
end
-
end
-
1
class Hash
-
# Return a hash that includes everything but the given keys. This is useful for
-
# limiting a set of parameters to everything but a few known toggles:
-
#
-
# @person.update_attributes(params[:person].except(:admin))
-
1
def except(*keys)
-
194
dup.except!(*keys)
-
end
-
-
# Replaces the hash without the given keys.
-
1
def except!(*keys)
-
1606
keys.each { |key| delete(key) }
-
194
self
-
end
-
end
-
1
require 'active_support/hash_with_indifferent_access'
-
-
1
class Hash
-
-
# Returns an <tt>ActiveSupport::HashWithIndifferentAccess</tt> out of its receiver:
-
#
-
# { a: 1 }.with_indifferent_access['a'] # => 1
-
1
def with_indifferent_access
-
2
ActiveSupport::HashWithIndifferentAccess.new_from_hash_copying_default(self)
-
end
-
-
# Called when object is nested under an object that receives
-
# #with_indifferent_access. This method will be called on the current object
-
# by the enclosing object and is aliased to #with_indifferent_access by
-
# default. Subclasses of Hash may overwrite this method to return +self+ if
-
# converting to an <tt>ActiveSupport::HashWithIndifferentAccess</tt> would not be
-
# desirable.
-
#
-
# b = { b: 1 }
-
# { a: b }.with_indifferent_access['a'] # calls b.nested_under_indifferent_access
-
1
alias nested_under_indifferent_access with_indifferent_access
-
end
-
1
class Hash
-
# Return a new hash with all keys converted using the block operation.
-
#
-
# hash = { name: 'Rob', age: '28' }
-
#
-
# hash.transform_keys{ |key| key.to_s.upcase }
-
# # => { "NAME" => "Rob", "AGE" => "28" }
-
1
def transform_keys
-
14
result = {}
-
14
each_key do |key|
-
22
result[yield(key)] = self[key]
-
end
-
14
result
-
end
-
-
# Destructively convert all keys using the block operations.
-
# Same as transform_keys but modifies +self+.
-
1
def transform_keys!
-
keys.each do |key|
-
self[yield(key)] = delete(key)
-
end
-
self
-
end
-
-
# Return a new hash with all keys converted to strings.
-
#
-
# hash = { name: 'Rob', age: '28' }
-
#
-
# hash.stringify_keys
-
# #=> { "name" => "Rob", "age" => "28" }
-
1
def stringify_keys
-
transform_keys{ |key| key.to_s }
-
end
-
-
# Destructively convert all keys to strings. Same as
-
# +stringify_keys+, but modifies +self+.
-
1
def stringify_keys!
-
transform_keys!{ |key| key.to_s }
-
end
-
-
# Return a new hash with all keys converted to symbols, as long as
-
# they respond to +to_sym+.
-
#
-
# hash = { 'name' => 'Rob', 'age' => '28' }
-
#
-
# hash.symbolize_keys
-
# #=> { name: "Rob", age: "28" }
-
1
def symbolize_keys
-
36
transform_keys{ |key| key.to_sym rescue key }
-
end
-
1
alias_method :to_options, :symbolize_keys
-
-
# Destructively convert all keys to symbols, as long as they respond
-
# to +to_sym+. Same as +symbolize_keys+, but modifies +self+.
-
1
def symbolize_keys!
-
transform_keys!{ |key| key.to_sym rescue key }
-
end
-
1
alias_method :to_options!, :symbolize_keys!
-
-
# Validate all keys in a hash match <tt>*valid_keys</tt>, raising ArgumentError
-
# on a mismatch. Note that keys are NOT treated indifferently, meaning if you
-
# use strings for keys but assert symbols as keys, this will fail.
-
#
-
# { name: 'Rob', years: '28' }.assert_valid_keys(:name, :age) # => raises "ArgumentError: Unknown key: years"
-
# { name: 'Rob', age: '28' }.assert_valid_keys('name', 'age') # => raises "ArgumentError: Unknown key: name"
-
# { name: 'Rob', age: '28' }.assert_valid_keys(:name, :age) # => passes, raises nothing
-
1
def assert_valid_keys(*valid_keys)
-
valid_keys.flatten!
-
each_key do |k|
-
raise ArgumentError.new("Unknown key: #{k}") unless valid_keys.include?(k)
-
end
-
end
-
-
# Return a new hash with all keys converted by the block operation.
-
# This includes the keys from the root hash and from all
-
# nested hashes.
-
#
-
# hash = { person: { name: 'Rob', age: '28' } }
-
#
-
# hash.deep_transform_keys{ |key| key.to_s.upcase }
-
# # => { "PERSON" => { "NAME" => "Rob", "AGE" => "28" } }
-
1
def deep_transform_keys(&block)
-
47
result = {}
-
47
each do |key, value|
-
127
result[yield(key)] = value.is_a?(Hash) ? value.deep_transform_keys(&block) : value
-
end
-
47
result
-
end
-
-
# Destructively convert all keys by using the block operation.
-
# This includes the keys from the root hash and from all
-
# nested hashes.
-
1
def deep_transform_keys!(&block)
-
keys.each do |key|
-
value = delete(key)
-
self[yield(key)] = value.is_a?(Hash) ? value.deep_transform_keys!(&block) : value
-
end
-
self
-
end
-
-
# Return a new hash with all keys converted to strings.
-
# This includes the keys from the root hash and from all
-
# nested hashes.
-
#
-
# hash = { person: { name: 'Rob', age: '28' } }
-
#
-
# hash.deep_stringify_keys
-
# # => { "person" => { "name" => "Rob", "age" => "28" } }
-
1
def deep_stringify_keys
-
deep_transform_keys{ |key| key.to_s }
-
end
-
-
# Destructively convert all keys to strings.
-
# This includes the keys from the root hash and from all
-
# nested hashes.
-
1
def deep_stringify_keys!
-
deep_transform_keys!{ |key| key.to_s }
-
end
-
-
# Return a new hash with all keys converted to symbols, as long as
-
# they respond to +to_sym+. This includes the keys from the root hash
-
# and from all nested hashes.
-
#
-
# hash = { 'person' => { 'name' => 'Rob', 'age' => '28' } }
-
#
-
# hash.deep_symbolize_keys
-
# # => { person: { name: "Rob", age: "28" } }
-
1
def deep_symbolize_keys
-
132
deep_transform_keys{ |key| key.to_sym rescue key }
-
end
-
-
# Destructively convert all keys to symbols, as long as they respond
-
# to +to_sym+. This includes the keys from the root hash and from all
-
# nested hashes.
-
1
def deep_symbolize_keys!
-
deep_transform_keys!{ |key| key.to_sym rescue key }
-
end
-
end
-
1
class Hash
-
# Merges the caller into +other_hash+. For example,
-
#
-
# options = options.reverse_merge(size: 25, velocity: 10)
-
#
-
# is equivalent to
-
#
-
# options = { size: 25, velocity: 10 }.merge(options)
-
#
-
# This is particularly useful for initializing an options hash
-
# with default values.
-
1
def reverse_merge(other_hash)
-
119
other_hash.merge(self)
-
end
-
-
# Destructive +reverse_merge+.
-
1
def reverse_merge!(other_hash)
-
# right wins if there is no left
-
39
merge!( other_hash ){|key,left,right| left }
-
end
-
1
alias_method :reverse_update, :reverse_merge!
-
end
-
1
class Hash
-
# Slice a hash to include only the given keys. This is useful for
-
# limiting an options hash to valid keys before passing to a method:
-
#
-
# def search(criteria = {})
-
# criteria.assert_valid_keys(:mass, :velocity, :time)
-
# end
-
#
-
# search(options.slice(:mass, :velocity, :time))
-
#
-
# If you have an array of keys you want to limit to, you should splat them:
-
#
-
# valid_keys = [:mass, :velocity, :time]
-
# search(options.slice(*valid_keys))
-
1
def slice(*keys)
-
keys.map! { |key| convert_key(key) } if respond_to?(:convert_key, true)
-
keys.each_with_object(self.class.new) { |k, hash| hash[k] = self[k] if has_key?(k) }
-
end
-
-
# Replaces the hash with only the given keys.
-
# Returns a hash containing the removed key/value pairs.
-
#
-
# { a: 1, b: 2, c: 3, d: 4 }.slice!(:a, :b)
-
# # => {:c=>3, :d=>4}
-
1
def slice!(*keys)
-
keys.map! { |key| convert_key(key) } if respond_to?(:convert_key, true)
-
omit = slice(*self.keys - keys)
-
hash = slice(*keys)
-
replace(hash)
-
omit
-
end
-
-
# Removes and returns the key/value pairs matching the given keys.
-
#
-
# { a: 1, b: 2, c: 3, d: 4 }.extract!(:a, :b) # => {:a=>1, :b=>2}
-
# { a: 1, b: 2 }.extract!(:a, :x) # => {:a=>1}
-
1
def extract!(*keys)
-
keys.each_with_object(self.class.new) { |key, result| result[key] = delete(key) if has_key?(key) }
-
end
-
end
-
1
class Integer
-
# Enables the use of time calculations and declarations, like <tt>45.minutes +
-
# 2.hours + 4.years</tt>.
-
#
-
# These methods use Time#advance for precise date calculations when using
-
# <tt>from_now</tt>, +ago+, etc. as well as adding or subtracting their
-
# results from a Time object.
-
#
-
# # equivalent to Time.now.advance(months: 1)
-
# 1.month.from_now
-
#
-
# # equivalent to Time.now.advance(years: 2)
-
# 2.years.from_now
-
#
-
# # equivalent to Time.now.advance(months: 4, years: 5)
-
# (4.months + 5.years).from_now
-
#
-
# While these methods provide precise calculation when used as in the examples
-
# above, care should be taken to note that this is not true if the result of
-
# +months+, +years+, etc is converted before use:
-
#
-
# # equivalent to 30.days.to_i.from_now
-
# 1.month.to_i.from_now
-
#
-
# # equivalent to 365.25.days.to_f.from_now
-
# 1.year.to_f.from_now
-
#
-
# In such cases, Ruby's core
-
# Date[http://ruby-doc.org/stdlib/libdoc/date/rdoc/Date.html] and
-
# Time[http://ruby-doc.org/stdlib/libdoc/time/rdoc/Time.html] should be used for precision
-
# date and time arithmetic.
-
1
def months
-
ActiveSupport::Duration.new(self * 30.days, [[:months, self]])
-
end
-
1
alias :month :months
-
-
1
def years
-
ActiveSupport::Duration.new(self * 365.25.days, [[:years, self]])
-
end
-
1
alias :year :years
-
end
-
1
module Kernel
-
# class_eval on an object acts like singleton_class.class_eval.
-
1
def class_eval(*args, &block)
-
singleton_class.class_eval(*args, &block)
-
end
-
end
-
1
class LoadError
-
1
REGEXPS = [
-
/^no such file to load -- (.+)$/i,
-
/^Missing \w+ (?:file\s*)?([^\s]+.rb)$/i,
-
/^Missing API definition file in (.+)$/i,
-
/^cannot load such file -- (.+)$/i,
-
]
-
-
1
unless method_defined?(:path)
-
1
def path
-
@path ||= begin
-
REGEXPS.find do |regex|
-
message =~ regex
-
end
-
$1
-
23
end
-
end
-
end
-
-
1
def is_missing?(location)
-
23
location.sub(/\.rb$/, '') == path.sub(/\.rb$/, '')
-
end
-
end
-
-
1
MissingSourceFile = LoadError
-
1
require 'active_support/core_ext/module/aliasing'
-
1
require 'active_support/core_ext/module/introspection'
-
1
require 'active_support/core_ext/module/anonymous'
-
1
require 'active_support/core_ext/module/reachable'
-
1
require 'active_support/core_ext/module/attribute_accessors'
-
1
require 'active_support/core_ext/module/attr_internal'
-
1
require 'active_support/core_ext/module/delegation'
-
1
require 'active_support/core_ext/module/deprecation'
-
1
require 'active_support/core_ext/module/remove_method'
-
1
require 'active_support/core_ext/module/qualified_const'
-
1
class Module
-
# Encapsulates the common pattern of:
-
#
-
# alias_method :foo_without_feature, :foo
-
# alias_method :foo, :foo_with_feature
-
#
-
# With this, you simply do:
-
#
-
# alias_method_chain :foo, :feature
-
#
-
# And both aliases are set up for you.
-
#
-
# Query and bang methods (foo?, foo!) keep the same punctuation:
-
#
-
# alias_method_chain :foo?, :feature
-
#
-
# is equivalent to
-
#
-
# alias_method :foo_without_feature?, :foo?
-
# alias_method :foo?, :foo_with_feature?
-
#
-
# so you can safely chain foo, foo?, and foo! with the same feature.
-
1
def alias_method_chain(target, feature)
-
# Strip out punctuation on predicates or bang methods since
-
# e.g. target?_without_feature is not a valid method name.
-
aliased_target, punctuation = target.to_s.sub(/([?!=])$/, ''), $1
-
yield(aliased_target, punctuation) if block_given?
-
-
with_method = "#{aliased_target}_with_#{feature}#{punctuation}"
-
without_method = "#{aliased_target}_without_#{feature}#{punctuation}"
-
-
alias_method without_method, target
-
alias_method target, with_method
-
-
case
-
when public_method_defined?(without_method)
-
public target
-
when protected_method_defined?(without_method)
-
protected target
-
when private_method_defined?(without_method)
-
private target
-
end
-
end
-
-
# Allows you to make aliases for attributes, which includes
-
# getter, setter, and query methods.
-
#
-
# class Content < ActiveRecord::Base
-
# # has a title attribute
-
# end
-
#
-
# class Email < Content
-
# alias_attribute :subject, :title
-
# end
-
#
-
# e = Email.find(1)
-
# e.title # => "Superstars"
-
# e.subject # => "Superstars"
-
# e.subject? # => true
-
# e.subject = "Megastars"
-
# e.title # => "Megastars"
-
1
def alias_attribute(new_name, old_name)
-
module_eval <<-STR, __FILE__, __LINE__ + 1
-
def #{new_name}; self.#{old_name}; end # def subject; self.title; end
-
def #{new_name}?; self.#{old_name}?; end # def subject?; self.title?; end
-
def #{new_name}=(v); self.#{old_name} = v; end # def subject=(v); self.title = v; end
-
STR
-
end
-
end
-
1
class Module
-
# A module may or may not have a name.
-
#
-
# module M; end
-
# M.name # => "M"
-
#
-
# m = Module.new
-
# m.name # => nil
-
#
-
# A module gets a name when it is first assigned to a constant. Either
-
# via the +module+ or +class+ keyword or by an explicit assignment:
-
#
-
# m = Module.new # creates an anonymous module
-
# M = m # => m gets a name here as a side-effect
-
# m.name # => "M"
-
1
def anonymous?
-
57
name.nil?
-
end
-
end
-
1
class Module
-
# Declares an attribute reader backed by an internally-named instance variable.
-
1
def attr_internal_reader(*attrs)
-
20
attrs.each {|attr_name| attr_internal_define(attr_name, :reader)}
-
end
-
-
# Declares an attribute writer backed by an internally-named instance variable.
-
1
def attr_internal_writer(*attrs)
-
26
attrs.each {|attr_name| attr_internal_define(attr_name, :writer)}
-
end
-
-
# Declares an attribute reader and writer backed by an internally-named instance
-
# variable.
-
1
def attr_internal_accessor(*attrs)
-
8
attr_internal_reader(*attrs)
-
8
attr_internal_writer(*attrs)
-
end
-
1
alias_method :attr_internal, :attr_internal_accessor
-
-
2
class << self; attr_accessor :attr_internal_naming_format end
-
1
self.attr_internal_naming_format = '@_%s'
-
-
1
private
-
1
def attr_internal_ivar_name(attr)
-
27
Module.attr_internal_naming_format % attr
-
end
-
-
1
def attr_internal_define(attr_name, type)
-
27
internal_name = attr_internal_ivar_name(attr_name).sub(/\A@/, '')
-
27
class_eval do # class_eval is necessary on 1.9 or else the methods a made private
-
# use native attr_* methods as they are faster on some Ruby implementations
-
27
send("attr_#{type}", internal_name)
-
end
-
27
attr_name, internal_name = "#{attr_name}=", "#{internal_name}=" if type == :writer
-
27
alias_method attr_name, internal_name
-
27
remove_method internal_name
-
end
-
end
-
1
require 'active_support/core_ext/array/extract_options'
-
-
1
class Module
-
1
def mattr_reader(*syms)
-
20
options = syms.extract_options!
-
20
syms.each do |sym|
-
20
raise NameError.new('invalid attribute name') unless sym =~ /^[_A-Za-z]\w*$/
-
20
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
-
@@#{sym} = nil unless defined? @@#{sym}
-
-
def self.#{sym}
-
@@#{sym}
-
end
-
EOS
-
-
20
unless options[:instance_reader] == false || options[:instance_accessor] == false
-
20
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
-
def #{sym}
-
@@#{sym}
-
end
-
EOS
-
end
-
end
-
end
-
-
1
def mattr_writer(*syms)
-
21
options = syms.extract_options!
-
21
syms.each do |sym|
-
21
raise NameError.new('invalid attribute name') unless sym =~ /^[_A-Za-z]\w*$/
-
21
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
-
def self.#{sym}=(obj)
-
@@#{sym} = obj
-
end
-
EOS
-
-
21
unless options[:instance_writer] == false || options[:instance_accessor] == false
-
21
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
-
def #{sym}=(obj)
-
@@#{sym} = obj
-
end
-
EOS
-
end
-
end
-
end
-
-
# Extends the module object with module and instance accessors for class attributes,
-
# just like the native attr* accessors for instance attributes.
-
#
-
# module AppConfiguration
-
# mattr_accessor :google_api_key
-
#
-
# self.google_api_key = "123456789"
-
# end
-
#
-
# AppConfiguration.google_api_key # => "123456789"
-
# AppConfiguration.google_api_key = "overriding the api key!"
-
# AppConfiguration.google_api_key # => "overriding the api key!"
-
#
-
# To opt out of the instance writer method, pass <tt>instance_writer: false</tt>.
-
# To opt out of the instance reader method, pass <tt>instance_reader: false</tt>.
-
# To opt out of both instance methods, pass <tt>instance_accessor: false</tt>.
-
1
def mattr_accessor(*syms)
-
20
mattr_reader(*syms)
-
20
mattr_writer(*syms)
-
end
-
end
-
1
class Module
-
# Provides a delegate class method to easily expose contained objects' public methods
-
# as your own. Pass one or more methods (specified as symbols or strings)
-
# and the name of the target object via the <tt>:to</tt> option (also a symbol
-
# or string). At least one method and the <tt>:to</tt> option are required.
-
#
-
# Delegation is particularly useful with Active Record associations:
-
#
-
# class Greeter < ActiveRecord::Base
-
# def hello
-
# 'hello'
-
# end
-
#
-
# def goodbye
-
# 'goodbye'
-
# end
-
# end
-
#
-
# class Foo < ActiveRecord::Base
-
# belongs_to :greeter
-
# delegate :hello, to: :greeter
-
# end
-
#
-
# Foo.new.hello # => "hello"
-
# Foo.new.goodbye # => NoMethodError: undefined method `goodbye' for #<Foo:0x1af30c>
-
#
-
# Multiple delegates to the same target are allowed:
-
#
-
# class Foo < ActiveRecord::Base
-
# belongs_to :greeter
-
# delegate :hello, :goodbye, to: :greeter
-
# end
-
#
-
# Foo.new.goodbye # => "goodbye"
-
#
-
# Methods can be delegated to instance variables, class variables, or constants
-
# by providing them as a symbols:
-
#
-
# class Foo
-
# CONSTANT_ARRAY = [0,1,2,3]
-
# @@class_array = [4,5,6,7]
-
#
-
# def initialize
-
# @instance_array = [8,9,10,11]
-
# end
-
# delegate :sum, to: :CONSTANT_ARRAY
-
# delegate :min, to: :@@class_array
-
# delegate :max, to: :@instance_array
-
# end
-
#
-
# Foo.new.sum # => 6
-
# Foo.new.min # => 4
-
# Foo.new.max # => 11
-
#
-
# It's also possible to delegate a method to the class by using +:class+:
-
#
-
# class Foo
-
# def self.hello
-
# "world"
-
# end
-
#
-
# delegate :hello, to: :class
-
# end
-
#
-
# Foo.new.hello # => "world"
-
#
-
# Delegates can optionally be prefixed using the <tt>:prefix</tt> option. If the value
-
# is <tt>true</tt>, the delegate methods are prefixed with the name of the object being
-
# delegated to.
-
#
-
# Person = Struct.new(:name, :address)
-
#
-
# class Invoice < Struct.new(:client)
-
# delegate :name, :address, to: :client, prefix: true
-
# end
-
#
-
# john_doe = Person.new('John Doe', 'Vimmersvej 13')
-
# invoice = Invoice.new(john_doe)
-
# invoice.client_name # => "John Doe"
-
# invoice.client_address # => "Vimmersvej 13"
-
#
-
# It is also possible to supply a custom prefix.
-
#
-
# class Invoice < Struct.new(:client)
-
# delegate :name, :address, to: :client, prefix: :customer
-
# end
-
#
-
# invoice = Invoice.new(john_doe)
-
# invoice.customer_name # => 'John Doe'
-
# invoice.customer_address # => 'Vimmersvej 13'
-
#
-
# If the delegate object is +nil+ an exception is raised, and that happens
-
# no matter whether +nil+ responds to the delegated method. You can get a
-
# +nil+ instead with the +:allow_nil+ option.
-
#
-
# class Foo
-
# attr_accessor :bar
-
# def initialize(bar = nil)
-
# @bar = bar
-
# end
-
# delegate :zoo, to: :bar
-
# end
-
#
-
# Foo.new.zoo # raises NoMethodError exception (you called nil.zoo)
-
#
-
# class Foo
-
# attr_accessor :bar
-
# def initialize(bar = nil)
-
# @bar = bar
-
# end
-
# delegate :zoo, to: :bar, allow_nil: true
-
# end
-
#
-
# Foo.new.zoo # returns nil
-
1
def delegate(*methods)
-
44
options = methods.pop
-
44
unless options.is_a?(Hash) && to = options[:to]
-
raise ArgumentError, 'Delegation needs a target. Supply an options hash with a :to key as the last argument (e.g. delegate :hello, to: :greeter).'
-
end
-
-
44
prefix, allow_nil = options.values_at(:prefix, :allow_nil)
-
-
44
if prefix == true && to =~ /^[^a-z_]/
-
raise ArgumentError, 'Can only automatically set the delegation prefix when delegating to a method.'
-
end
-
-
44
method_prefix = \
-
if prefix
-
"#{prefix == true ? to : prefix}_"
-
else
-
44
''
-
end
-
-
44
file, line = caller.first.split(':', 2)
-
44
line = line.to_i
-
-
44
to = to.to_s
-
44
to = 'self.class' if to == 'class'
-
-
44
methods.each do |method|
-
# Attribute writer methods only accept one argument. Makes sure []=
-
# methods still accept two arguments.
-
95
definition = (method =~ /[^\]]=$/) ? 'arg' : '*args, &block'
-
-
95
if allow_nil
-
6
module_eval(<<-EOS, file, line - 2)
-
def #{method_prefix}#{method}(#{definition}) # def customer_name(*args, &block)
-
if #{to} || #{to}.respond_to?(:#{method}) # if client || client.respond_to?(:name)
-
#{to}.#{method}(#{definition}) # client.name(*args, &block)
-
end # end
-
end # end
-
EOS
-
else
-
89
exception = %(raise "#{self}##{method_prefix}#{method} delegated to #{to}.#{method}, but #{to} is nil: \#{self.inspect}")
-
-
89
module_eval(<<-EOS, file, line - 1)
-
def #{method_prefix}#{method}(#{definition}) # def customer_name(*args, &block)
-
#{to}.#{method}(#{definition}) # client.name(*args, &block)
-
rescue NoMethodError # rescue NoMethodError
-
if #{to}.nil? # if client.nil?
-
#{exception} # # add helpful message to the exception
-
else # else
-
raise # raise
-
end # end
-
end # end
-
EOS
-
end
-
end
-
end
-
end
-
1
require 'active_support/deprecation/method_wrappers'
-
-
1
class Module
-
# deprecate :foo
-
# deprecate bar: 'message'
-
# deprecate :foo, :bar, baz: 'warning!', qux: 'gone!'
-
#
-
# You can also use custom deprecator instance:
-
#
-
# deprecate :foo, deprecator: MyLib::Deprecator.new
-
# deprecate :foo, bar: "warning!", deprecator: MyLib::Deprecator.new
-
#
-
# \Custom deprecators must respond to <tt>deprecation_warning(deprecated_method_name, message, caller_backtrace)</tt>
-
# method where you can implement your custom warning behavior.
-
#
-
# class MyLib::Deprecator
-
# def deprecation_warning(deprecated_method_name, message, caller_backtrace)
-
# message = "#{method_name} is deprecated and will be removed from MyLibrary | #{message}"
-
# Kernel.warn message
-
# end
-
# end
-
1
def deprecate(*method_names)
-
ActiveSupport::Deprecation.deprecate_methods(self, *method_names)
-
end
-
end
-
1
require 'active_support/inflector'
-
-
1
class Module
-
# Returns the name of the module containing this one.
-
#
-
# M::N.parent_name # => "M"
-
1
def parent_name
-
2
if defined? @parent_name
-
1
@parent_name
-
else
-
1
@parent_name = name =~ /::[^:]+\Z/ ? $`.freeze : nil
-
end
-
end
-
-
# Returns the module which contains this one according to its name.
-
#
-
# module M
-
# module N
-
# end
-
# end
-
# X = M::N
-
#
-
# M::N.parent # => M
-
# X.parent # => M
-
#
-
# The parent of top-level and anonymous modules is Object.
-
#
-
# M.parent # => Object
-
# Module.new.parent # => Object
-
1
def parent
-
2
parent_name ? ActiveSupport::Inflector.constantize(parent_name) : Object
-
end
-
-
# Returns all the parents of this module according to its name, ordered from
-
# nested outwards. The receiver is not contained within the result.
-
#
-
# module M
-
# module N
-
# end
-
# end
-
# X = M::N
-
#
-
# M.parents # => [Object]
-
# M::N.parents # => [M, Object]
-
# X.parents # => [M, Object]
-
1
def parents
-
parents = []
-
if parent_name
-
parts = parent_name.split('::')
-
until parts.empty?
-
parents << ActiveSupport::Inflector.constantize(parts * '::')
-
parts.pop
-
end
-
end
-
parents << Object unless parents.include? Object
-
parents
-
end
-
-
1
def local_constants #:nodoc:
-
constants(false)
-
end
-
-
# *DEPRECATED*: Use +local_constants+ instead.
-
#
-
# Returns the names of the constants defined locally as strings.
-
#
-
# module M
-
# X = 1
-
# end
-
# M.local_constant_names # => ["X"]
-
#
-
# This method is useful for forward compatibility, since Ruby 1.8 returns
-
# constant names as strings, whereas 1.9 returns them as symbols.
-
1
def local_constant_names
-
ActiveSupport::Deprecation.warn 'Module#local_constant_names is deprecated, use Module#local_constants instead'
-
local_constants.map { |c| c.to_s }
-
end
-
end
-
1
require 'active_support/core_ext/string/inflections'
-
-
#--
-
# Allows code reuse in the methods below without polluting Module.
-
#++
-
1
module QualifiedConstUtils
-
1
def self.raise_if_absolute(path)
-
2
raise NameError.new("wrong constant name #$&") if path =~ /\A::[^:]+/
-
end
-
-
1
def self.names(path)
-
2
path.split('::')
-
end
-
end
-
-
##
-
# Extends the API for constants to be able to deal with qualified names. Arguments
-
# are assumed to be relative to the receiver.
-
#
-
#--
-
# Qualified names are required to be relative because we are extending existing
-
# methods that expect constant names, ie, relative paths of length 1. For example,
-
# Object.const_get('::String') raises NameError and so does qualified_const_get.
-
#++
-
1
class Module
-
1
def qualified_const_defined?(path, search_parents=true)
-
2
QualifiedConstUtils.raise_if_absolute(path)
-
-
2
QualifiedConstUtils.names(path).inject(self) do |mod, name|
-
2
return unless mod.const_defined?(name, search_parents)
-
2
mod.const_get(name)
-
end
-
2
return true
-
end
-
-
1
def qualified_const_get(path)
-
QualifiedConstUtils.raise_if_absolute(path)
-
-
QualifiedConstUtils.names(path).inject(self) do |mod, name|
-
mod.const_get(name)
-
end
-
end
-
-
1
def qualified_const_set(path, value)
-
QualifiedConstUtils.raise_if_absolute(path)
-
-
const_name = path.demodulize
-
mod_name = path.deconstantize
-
mod = mod_name.empty? ? self : qualified_const_get(mod_name)
-
mod.const_set(const_name, value)
-
end
-
end
-
1
require 'active_support/core_ext/module/anonymous'
-
1
require 'active_support/core_ext/string/inflections'
-
-
1
class Module
-
1
def reachable? #:nodoc:
-
!anonymous? && name.safe_constantize.equal?(self)
-
end
-
end
-
1
class Module
-
1
def remove_possible_method(method)
-
408
if method_defined?(method) || private_method_defined?(method)
-
346
undef_method(method)
-
end
-
end
-
-
1
def redefine_method(method, &block)
-
17
remove_possible_method(method)
-
17
define_method(method, &block)
-
end
-
end
-
1
class NameError
-
# Extract the name of the missing constant from the exception message.
-
1
def missing_name
-
if /undefined local variable or method/ !~ message
-
$1 if /((::)?([A-Z]\w*)(::[A-Z]\w*)*)$/ =~ message
-
end
-
end
-
-
# Was this exception raised because the given name was missing?
-
1
def missing_name?(name)
-
if name.is_a? Symbol
-
last_name = (missing_name || '').split('::').last
-
last_name == name.to_s
-
else
-
missing_name == name.to_s
-
end
-
end
-
end
-
1
require 'active_support/duration'
-
1
require 'active_support/core_ext/time/calculations'
-
1
require 'active_support/core_ext/time/acts_like'
-
-
1
class Numeric
-
# Enables the use of time calculations and declarations, like 45.minutes + 2.hours + 4.years.
-
#
-
# These methods use Time#advance for precise date calculations when using from_now, ago, etc.
-
# as well as adding or subtracting their results from a Time object. For example:
-
#
-
# # equivalent to Time.current.advance(months: 1)
-
# 1.month.from_now
-
#
-
# # equivalent to Time.current.advance(years: 2)
-
# 2.years.from_now
-
#
-
# # equivalent to Time.current.advance(months: 4, years: 5)
-
# (4.months + 5.years).from_now
-
#
-
# While these methods provide precise calculation when used as in the examples above, care
-
# should be taken to note that this is not true if the result of `months', `years', etc is
-
# converted before use:
-
#
-
# # equivalent to 30.days.to_i.from_now
-
# 1.month.to_i.from_now
-
#
-
# # equivalent to 365.25.days.to_f.from_now
-
# 1.year.to_f.from_now
-
#
-
# In such cases, Ruby's core
-
# Date[http://ruby-doc.org/stdlib/libdoc/date/rdoc/Date.html] and
-
# Time[http://ruby-doc.org/stdlib/libdoc/time/rdoc/Time.html] should be used for precision
-
# date and time arithmetic.
-
1
def seconds
-
ActiveSupport::Duration.new(self, [[:seconds, self]])
-
end
-
1
alias :second :seconds
-
-
1
def minutes
-
ActiveSupport::Duration.new(self * 60, [[:seconds, self * 60]])
-
end
-
1
alias :minute :minutes
-
-
1
def hours
-
1
ActiveSupport::Duration.new(self * 3600, [[:seconds, self * 3600]])
-
end
-
1
alias :hour :hours
-
-
1
def days
-
1
ActiveSupport::Duration.new(self * 24.hours, [[:days, self]])
-
end
-
1
alias :day :days
-
-
1
def weeks
-
ActiveSupport::Duration.new(self * 7.days, [[:days, self * 7]])
-
end
-
1
alias :week :weeks
-
-
1
def fortnights
-
ActiveSupport::Duration.new(self * 2.weeks, [[:days, self * 14]])
-
end
-
1
alias :fortnight :fortnights
-
-
# Reads best without arguments: 10.minutes.ago
-
1
def ago(time = ::Time.current)
-
time - self
-
end
-
-
# Reads best with argument: 10.minutes.until(time)
-
1
alias :until :ago
-
-
# Reads best with argument: 10.minutes.since(time)
-
1
def since(time = ::Time.current)
-
time + self
-
end
-
-
# Reads best without arguments: 10.minutes.from_now
-
1
alias :from_now :since
-
end
-
1
class Object
-
# A duck-type assistant method. For example, Active Support extends Date
-
# to define an acts_like_date? method, and extends Time to define
-
# acts_like_time?. As a result, we can do "x.acts_like?(:time)" and
-
# "x.acts_like?(:date)" to do duck-type-safe comparisons, since classes that
-
# we want to act like Time simply need to define an acts_like_time? method.
-
1
def acts_like?(duck)
-
1
respond_to? :"acts_like_#{duck}?"
-
end
-
end
-
# encoding: utf-8
-
-
1
class Object
-
# An object is blank if it's false, empty, or a whitespace string.
-
# For example, '', ' ', +nil+, [], and {} are all blank.
-
#
-
# This simplifies:
-
#
-
# if address.nil? || address.empty?
-
#
-
# ...to:
-
#
-
# if address.blank?
-
1
def blank?
-
respond_to?(:empty?) ? empty? : !self
-
end
-
-
# An object is present if it's not <tt>blank?</tt>.
-
1
def present?
-
983
!blank?
-
end
-
-
# Returns object if it's <tt>present?</tt> otherwise returns +nil+.
-
# <tt>object.presence</tt> is equivalent to <tt>object.present? ? object : nil</tt>.
-
#
-
# This is handy for any representation of objects where blank is the same
-
# as not present at all. For example, this simplifies a common check for
-
# HTTP POST/query parameters:
-
#
-
# state = params[:state] if params[:state].present?
-
# country = params[:country] if params[:country].present?
-
# region = state || country || 'US'
-
#
-
# ...becomes:
-
#
-
# region = params[:state].presence || params[:country].presence || 'US'
-
1
def presence
-
313
self if present?
-
end
-
end
-
-
1
class NilClass
-
# +nil+ is blank:
-
#
-
# nil.blank? # => true
-
1
def blank?
-
true
-
end
-
end
-
-
1
class FalseClass
-
# +false+ is blank:
-
#
-
# false.blank? # => true
-
1
def blank?
-
true
-
end
-
end
-
-
1
class TrueClass
-
# +true+ is not blank:
-
#
-
# true.blank? # => false
-
1
def blank?
-
false
-
end
-
end
-
-
1
class Array
-
# An array is blank if it's empty:
-
#
-
# [].blank? # => true
-
# [1,2,3].blank? # => false
-
1
alias_method :blank?, :empty?
-
end
-
-
1
class Hash
-
# A hash is blank if it's empty:
-
#
-
# {}.blank? # => true
-
# { key: 'value' }.blank? # => false
-
1
alias_method :blank?, :empty?
-
end
-
-
1
class String
-
# A string is blank if it's empty or contains whitespaces only:
-
#
-
# ''.blank? # => true
-
# ' '.blank? # => true
-
# ' '.blank? # => true
-
# ' something here '.blank? # => false
-
1
def blank?
-
4118
self !~ /[^[:space:]]/
-
end
-
end
-
-
1
class Numeric #:nodoc:
-
# No number is blank:
-
#
-
# 1.blank? # => false
-
# 0.blank? # => false
-
1
def blank?
-
false
-
end
-
end
-
#--
-
# Most objects are cloneable, but not all. For example you can't dup +nil+:
-
#
-
# nil.dup # => TypeError: can't dup NilClass
-
#
-
# Classes may signal their instances are not duplicable removing +dup+/+clone+
-
# or raising exceptions from them. So, to dup an arbitrary object you normally
-
# use an optimistic approach and are ready to catch an exception, say:
-
#
-
# arbitrary_object.dup rescue object
-
#
-
# Rails dups objects in a few critical spots where they are not that arbitrary.
-
# That rescue is very expensive (like 40 times slower than a predicate), and it
-
# is often triggered.
-
#
-
# That's why we hardcode the following cases and check duplicable? instead of
-
# using that rescue idiom.
-
#++
-
1
class Object
-
# Can you safely dup this object?
-
#
-
# False for +nil+, +false+, +true+, symbol, and number objects;
-
# true otherwise.
-
1
def duplicable?
-
true
-
end
-
end
-
-
1
class NilClass
-
# +nil+ is not duplicable:
-
#
-
# nil.duplicable? # => false
-
# nil.dup # => TypeError: can't dup NilClass
-
1
def duplicable?
-
false
-
end
-
end
-
-
1
class FalseClass
-
# +false+ is not duplicable:
-
#
-
# false.duplicable? # => false
-
# false.dup # => TypeError: can't dup FalseClass
-
1
def duplicable?
-
false
-
end
-
end
-
-
1
class TrueClass
-
# +true+ is not duplicable:
-
#
-
# true.duplicable? # => false
-
# true.dup # => TypeError: can't dup TrueClass
-
1
def duplicable?
-
false
-
end
-
end
-
-
1
class Symbol
-
# Symbols are not duplicable:
-
#
-
# :my_symbol.duplicable? # => false
-
# :my_symbol.dup # => TypeError: can't dup Symbol
-
1
def duplicable?
-
false
-
end
-
end
-
-
1
class Numeric
-
# Numbers are not duplicable:
-
#
-
# 3.duplicable? # => false
-
# 3.dup # => TypeError: can't dup Fixnum
-
1
def duplicable?
-
false
-
end
-
end
-
-
1
require 'bigdecimal'
-
1
class BigDecimal
-
1
begin
-
1
BigDecimal.new('4.56').dup
-
-
def duplicable?
-
true
-
end
-
rescue TypeError
-
# can't dup, so use superclass implementation
-
end
-
end
-
1
class Object
-
# Returns true if this object is included in the argument(s). Argument must be
-
# any object which responds to +#include?+ or optionally, multiple arguments can be passed in. Usage:
-
#
-
# characters = ['Konata', 'Kagami', 'Tsukasa']
-
# 'Konata'.in?(characters) # => true
-
#
-
# character = 'Konata'
-
# character.in?('Konata', 'Kagami', 'Tsukasa') # => true
-
#
-
# This will throw an ArgumentError if a single argument is passed in and it doesn't respond
-
# to +#include?+.
-
1
def in?(*args)
-
if args.length > 1
-
args.include? self
-
else
-
another_object = args.first
-
if another_object.respond_to? :include?
-
another_object.include? self
-
else
-
raise ArgumentError.new 'The single parameter passed to #in? must respond to #include?'
-
end
-
end
-
end
-
end
-
1
class Object
-
# Returns a hash with string keys that maps instance variable names without "@" to their
-
# corresponding values.
-
#
-
# class C
-
# def initialize(x, y)
-
# @x, @y = x, y
-
# end
-
# end
-
#
-
# C.new(0, 1).instance_values # => {"x" => 0, "y" => 1}
-
1
def instance_values
-
Hash[instance_variables.map { |name| [name[1..-1], instance_variable_get(name)] }]
-
end
-
-
# Returns an array of instance variable names including "@".
-
#
-
# class C
-
# def initialize(x, y)
-
# @x, @y = x, y
-
# end
-
# end
-
#
-
# C.new(0, 1).instance_variable_names # => ["@y", "@x"]
-
1
def instance_variable_names
-
instance_variables.map { |var| var.to_s }
-
end
-
end
-
# Hack to load json gem first so we can overwrite its to_json.
-
1
begin
-
1
require 'json'
-
rescue LoadError
-
end
-
-
# The JSON gem adds a few modules to Ruby core classes containing :to_json definition, overwriting
-
# their default behavior. That said, we need to define the basic to_json method in all of them,
-
# otherwise they will always use to_json gem implementation, which is backwards incompatible in
-
# several cases (for instance, the JSON implementation for Hash does not work) with inheritance
-
# and consequently classes as ActiveSupport::OrderedHash cannot be serialized to json.
-
1
[Object, Array, FalseClass, Float, Hash, Integer, NilClass, String, TrueClass].each do |klass|
-
9
klass.class_eval do
-
# Dumps object in JSON (JavaScript Object Notation). See www.json.org for more info.
-
9
def to_json(options = nil)
-
ActiveSupport::JSON.encode(self, options)
-
end
-
end
-
end
-
-
1
module Process
-
1
class Status
-
1
def as_json(options = nil)
-
{ :exitstatus => exitstatus, :pid => pid }
-
end
-
end
-
end
-
1
class Object
-
# Alias of <tt>to_s</tt>.
-
1
def to_param
-
2
to_s
-
end
-
end
-
-
1
class NilClass
-
# Returns +self+.
-
1
def to_param
-
self
-
end
-
end
-
-
1
class TrueClass
-
# Returns +self+.
-
1
def to_param
-
self
-
end
-
end
-
-
1
class FalseClass
-
# Returns +self+.
-
1
def to_param
-
self
-
end
-
end
-
-
1
class Array
-
# Calls <tt>to_param</tt> on all its elements and joins the result with
-
# slashes. This is used by <tt>url_for</tt> in Action Pack.
-
1
def to_param
-
collect { |e| e.to_param }.join '/'
-
end
-
end
-
-
1
class Hash
-
# Returns a string representation of the receiver suitable for use as a URL
-
# query string:
-
#
-
# {name: 'David', nationality: 'Danish'}.to_param
-
# # => "name=David&nationality=Danish"
-
#
-
# An optional namespace can be passed to enclose the param names:
-
#
-
# {name: 'David', nationality: 'Danish'}.to_param('user')
-
# # => "user[name]=David&user[nationality]=Danish"
-
#
-
# The string pairs "key=value" that conform the query string
-
# are sorted lexicographically in ascending order.
-
#
-
# This method is also aliased as +to_query+.
-
1
def to_param(namespace = nil)
-
collect do |key, value|
-
value.to_query(namespace ? "#{namespace}[#{key}]" : key)
-
end.sort * '&'
-
end
-
end
-
1
require 'active_support/core_ext/object/to_param'
-
-
1
class Object
-
# Converts an object into a string suitable for use as a URL query string, using the given <tt>key</tt> as the
-
# param name.
-
#
-
# Note: This method is defined as a default implementation for all Objects for Hash#to_query to work.
-
1
def to_query(key)
-
require 'cgi' unless defined?(CGI) && defined?(CGI::escape)
-
"#{CGI.escape(key.to_param)}=#{CGI.escape(to_param.to_s)}"
-
end
-
end
-
-
1
class Array
-
# Converts an array into a string suitable for use as a URL query string,
-
# using the given +key+ as the param name.
-
#
-
# ['Rails', 'coding'].to_query('hobbies') # => "hobbies%5B%5D=Rails&hobbies%5B%5D=coding"
-
1
def to_query(key)
-
prefix = "#{key}[]"
-
collect { |value| value.to_query(prefix) }.join '&'
-
end
-
end
-
-
1
class Hash
-
1
alias_method :to_query, :to_param
-
end
-
1
class Object
-
# Invokes the public method identified by the symbol +method+, passing it any arguments
-
# and/or the block specified, just like the regular Ruby <tt>Object#public_send</tt> does.
-
#
-
# *Unlike* that method however, a +NoMethodError+ exception will *not* be raised
-
# and +nil+ will be returned instead, if the receiving object is a +nil+ object or NilClass.
-
#
-
# This is also true if the receiving object does not implemented the tried method. It will
-
# return +nil+ in that case as well.
-
#
-
# If try is called without a method to call, it will yield any given block with the object.
-
#
-
# Please also note that +try+ is defined on +Object+, therefore it won't work with
-
# subclasses of +BasicObject+. For example, using try with +SimpleDelegator+ will
-
# delegate +try+ to target instead of calling it on delegator itself.
-
#
-
# Without +try+
-
# @person && @person.name
-
# or
-
# @person ? @person.name : nil
-
#
-
# With +try+
-
# @person.try(:name)
-
#
-
# +try+ also accepts arguments and/or a block, for the method it is trying
-
# Person.try(:find, 1)
-
# @people.try(:collect) {|p| p.name}
-
#
-
# Without a method argument try will yield to the block unless the receiver is nil.
-
# @person.try { |p| "#{p.first_name} #{p.last_name}" }
-
#
-
# +try+ behaves like +Object#public_send+, unless called on +NilClass+.
-
1
def try(*a, &b)
-
12
if a.empty? && block_given?
-
yield self
-
else
-
12
public_send(*a, &b) if respond_to?(a.first)
-
end
-
end
-
-
# Same as #try, but will raise a NoMethodError exception if the receiving is not nil and
-
# does not implemented the tried method.
-
1
def try!(*a, &b)
-
if a.empty? && block_given?
-
yield self
-
else
-
public_send(*a, &b)
-
end
-
end
-
end
-
-
1
class NilClass
-
# Calling +try+ on +nil+ always returns +nil+.
-
# It becomes specially helpful when navigating through associations that may return +nil+.
-
#
-
# nil.try(:name) # => nil
-
#
-
# Without +try+
-
# @person && !@person.children.blank? && @person.children.first.name
-
#
-
# With +try+
-
# @person.try(:children).try(:first).try(:name)
-
1
def try(*args)
-
nil
-
end
-
-
1
def try!(*args)
-
nil
-
end
-
end
-
1
require 'active_support/option_merger'
-
-
1
class Object
-
# An elegant way to factor duplication out of options passed to a series of
-
# method calls. Each method called in the block, with the block variable as
-
# the receiver, will have its options merged with the default +options+ hash
-
# provided. Each method called on the block variable must take an options
-
# hash as its final argument.
-
#
-
# Without <tt>with_options></tt>, this code contains duplication:
-
#
-
# class Account < ActiveRecord::Base
-
# has_many :customers, dependent: :destroy
-
# has_many :products, dependent: :destroy
-
# has_many :invoices, dependent: :destroy
-
# has_many :expenses, dependent: :destroy
-
# end
-
#
-
# Using <tt>with_options</tt>, we can remove the duplication:
-
#
-
# class Account < ActiveRecord::Base
-
# with_options dependent: :destroy do |assoc|
-
# assoc.has_many :customers
-
# assoc.has_many :products
-
# assoc.has_many :invoices
-
# assoc.has_many :expenses
-
# end
-
# end
-
#
-
# It can also be used with an explicit receiver:
-
#
-
# I18n.with_options locale: user.locale, scope: 'newsletter' do |i18n|
-
# subject i18n.t :subject
-
# body i18n.t :body, user_name: user.name
-
# end
-
#
-
# <tt>with_options</tt> can also be nested since the call is forwarded to its receiver.
-
# Each nesting level will merge inherited defaults in addition to their own.
-
1
def with_options(options)
-
yield ActiveSupport::OptionMerger.new(self, options)
-
end
-
end
-
1
require "active_support/core_ext/kernel/singleton_class"
-
1
require "active_support/deprecation"
-
-
1
class Proc #:nodoc:
-
1
def bind(object)
-
ActiveSupport::Deprecation.warn 'Proc#bind is deprecated and will be removed in future versions'
-
-
block, time = self, Time.now
-
object.class_eval do
-
method_name = "__bind_#{time.to_i}_#{time.usec}"
-
define_method(method_name, &block)
-
method = instance_method(method_name)
-
remove_method(method_name)
-
method
-
end.bind(object)
-
end
-
end
-
1
class Regexp #:nodoc:
-
1
def multiline?
-
2
options & MULTILINE == MULTILINE
-
end
-
end
-
1
class String
-
# If you pass a single Fixnum, returns a substring of one character at that
-
# position. The first character of the string is at position 0, the next at
-
# position 1, and so on. If a range is supplied, a substring containing
-
# characters at offsets given by the range is returned. In both cases, if an
-
# offset is negative, it is counted from the end of the string. Returns nil
-
# if the initial offset falls outside the string. Returns an empty string if
-
# the beginning of the range is greater than the end of the string.
-
#
-
# str = "hello"
-
# str.at(0) #=> "h"
-
# str.at(1..3) #=> "ell"
-
# str.at(-2) #=> "l"
-
# str.at(-2..-1) #=> "lo"
-
# str.at(5) #=> nil
-
# str.at(5..-1) #=> ""
-
#
-
# If a Regexp is given, the matching portion of the string is returned.
-
# If a String is given, that given string is returned if it occurs in
-
# the string. In both cases, nil is returned if there is no match.
-
#
-
# str = "hello"
-
# str.at(/lo/) #=> "lo"
-
# str.at(/ol/) #=> nil
-
# str.at("lo") #=> "lo"
-
# str.at("ol") #=> nil
-
1
def at(position)
-
self[position]
-
end
-
-
# Returns a substring from the given position to the end of the string.
-
# If the position is negative, it is counted from the end of the string.
-
#
-
# str = "hello"
-
# str.from(0) #=> "hello"
-
# str.from(3) #=> "lo"
-
# str.from(-2) #=> "lo"
-
#
-
# You can mix it with +to+ method and do fun things like:
-
#
-
# str = "hello"
-
# str.from(0).to(-1) #=> "hello"
-
# str.from(1).to(-2) #=> "ell"
-
1
def from(position)
-
self[position..-1]
-
end
-
-
# Returns a substring from the beginning of the string to the given position.
-
# If the position is negative, it is counted from the end of the string.
-
#
-
# str = "hello"
-
# str.to(0) #=> "h"
-
# str.to(3) #=> "hell"
-
# str.to(-2) #=> "hell"
-
#
-
# You can mix it with +from+ method and do fun things like:
-
#
-
# str = "hello"
-
# str.from(0).to(-1) #=> "hello"
-
# str.from(1).to(-2) #=> "ell"
-
1
def to(position)
-
1
self[0..position]
-
end
-
-
# Returns the first character. If a limit is supplied, returns a substring
-
# from the beginning of the string until it reaches the limit value. If the
-
# given limit is greater than or equal to the string length, returns self.
-
#
-
# str = "hello"
-
# str.first #=> "h"
-
# str.first(1) #=> "h"
-
# str.first(2) #=> "he"
-
# str.first(0) #=> ""
-
# str.first(6) #=> "hello"
-
1
def first(limit = 1)
-
1
if limit == 0
-
''
-
1
elsif limit >= size
-
self
-
else
-
1
to(limit - 1)
-
end
-
end
-
-
# Returns the last character of the string. If a limit is supplied, returns a substring
-
# from the end of the string until it reaches the limit value (counting backwards). If
-
# the given limit is greater than or equal to the string length, returns self.
-
#
-
# str = "hello"
-
# str.last #=> "o"
-
# str.last(1) #=> "o"
-
# str.last(2) #=> "lo"
-
# str.last(0) #=> ""
-
# str.last(6) #=> "hello"
-
1
def last(limit = 1)
-
if limit == 0
-
''
-
elsif limit >= size
-
self
-
else
-
from(-limit)
-
end
-
end
-
end
-
1
class String
-
# Enable more predictable duck-typing on String-like classes. See <tt>Object#acts_like?</tt>.
-
1
def acts_like_string?
-
true
-
end
-
end
-
1
class String
-
# Returns the string, first removing all whitespace on both ends of
-
# the string, and then changing remaining consecutive whitespace
-
# groups into one space each.
-
#
-
# %{ Multi-line
-
# string }.squish # => "Multi-line string"
-
# " foo bar \n \t boo".squish # => "foo bar boo"
-
1
def squish
-
dup.squish!
-
end
-
-
# Performs a destructive squish. See String#squish.
-
1
def squish!
-
strip!
-
gsub!(/\s+/, ' ')
-
self
-
end
-
-
# Truncates a given +text+ after a given <tt>length</tt> if +text+ is longer than <tt>length</tt>:
-
#
-
# 'Once upon a time in a world far far away'.truncate(27)
-
# # => "Once upon a time in a wo..."
-
#
-
# Pass a string or regexp <tt>:separator</tt> to truncate +text+ at a natural break:
-
#
-
# 'Once upon a time in a world far far away'.truncate(27, separator: ' ')
-
# # => "Once upon a time in a..."
-
#
-
# 'Once upon a time in a world far far away'.truncate(27, separator: /\s/)
-
# # => "Once upon a time in a..."
-
#
-
# The last characters will be replaced with the <tt>:omission</tt> string (defaults to "...")
-
# for a total length not exceeding <tt>length</tt>:
-
#
-
# 'And they found that many people were sleeping better.'.truncate(25, omission: '... (continued)')
-
# # => "And they f... (continued)"
-
1
def truncate(truncate_at, options = {})
-
return dup unless length > truncate_at
-
-
options[:omission] ||= '...'
-
length_with_room_for_omission = truncate_at - options[:omission].length
-
stop = \
-
if options[:separator]
-
rindex(options[:separator], length_with_room_for_omission) || length_with_room_for_omission
-
else
-
length_with_room_for_omission
-
end
-
-
self[0...stop] + options[:omission]
-
end
-
end
-
1
require 'active_support/inflector/methods'
-
1
require 'active_support/inflector/transliterate'
-
-
# String inflections define new methods on the String class to transform names for different purposes.
-
# For instance, you can figure out the name of a table from the name of a class.
-
#
-
# 'ScaleScore'.tableize # => "scale_scores"
-
#
-
1
class String
-
# Returns the plural form of the word in the string.
-
#
-
# If the optional parameter +count+ is specified,
-
# the singular form will be returned if <tt>count == 1</tt>.
-
# For any other value of +count+ the plural will be returned.
-
#
-
# If the optional parameter +locale+ is specified,
-
# the word will be pluralized as a word of that language.
-
# By default, this parameter is set to <tt>:en</tt>.
-
# You must define your own inflection rules for languages other than English.
-
#
-
# 'post'.pluralize # => "posts"
-
# 'octopus'.pluralize # => "octopi"
-
# 'sheep'.pluralize # => "sheep"
-
# 'words'.pluralize # => "words"
-
# 'the blue mailman'.pluralize # => "the blue mailmen"
-
# 'CamelOctopus'.pluralize # => "CamelOctopi"
-
# 'apple'.pluralize(1) # => "apple"
-
# 'apple'.pluralize(2) # => "apples"
-
# 'ley'.pluralize(:es) # => "leyes"
-
# 'ley'.pluralize(1, :es) # => "ley"
-
1
def pluralize(count = nil, locale = :en)
-
locale = count if count.is_a?(Symbol)
-
if count == 1
-
self
-
else
-
ActiveSupport::Inflector.pluralize(self, locale)
-
end
-
end
-
-
# The reverse of +pluralize+, returns the singular form of a word in a string.
-
#
-
# If the optional parameter +locale+ is specified,
-
# the word will be singularized as a word of that language.
-
# By default, this paramter is set to <tt>:en</tt>.
-
# You must define your own inflection rules for languages other than English.
-
#
-
# 'posts'.singularize # => "post"
-
# 'octopi'.singularize # => "octopus"
-
# 'sheep'.singularize # => "sheep"
-
# 'word'.singularize # => "word"
-
# 'the blue mailmen'.singularize # => "the blue mailman"
-
# 'CamelOctopi'.singularize # => "CamelOctopus"
-
# 'leyes'.singularize(:es) # => "ley"
-
1
def singularize(locale = :en)
-
ActiveSupport::Inflector.singularize(self, locale)
-
end
-
-
# +constantize+ tries to find a declared constant with the name specified
-
# in the string. It raises a NameError when the name is not in CamelCase
-
# or is not initialized. See ActiveSupport::Inflector.constantize
-
#
-
# 'Module'.constantize # => Module
-
# 'Class'.constantize # => Class
-
# 'blargle'.constantize # => NameError: wrong constant name blargle
-
1
def constantize
-
30
ActiveSupport::Inflector.constantize(self)
-
end
-
-
# +safe_constantize+ tries to find a declared constant with the name specified
-
# in the string. It returns nil when the name is not in CamelCase
-
# or is not initialized. See ActiveSupport::Inflector.safe_constantize
-
#
-
# 'Module'.safe_constantize # => Module
-
# 'Class'.safe_constantize # => Class
-
# 'blargle'.safe_constantize # => nil
-
1
def safe_constantize
-
ActiveSupport::Inflector.safe_constantize(self)
-
end
-
-
# By default, +camelize+ converts strings to UpperCamelCase. If the argument to camelize
-
# is set to <tt>:lower</tt> then camelize produces lowerCamelCase.
-
#
-
# +camelize+ will also convert '/' to '::' which is useful for converting paths to namespaces.
-
#
-
# 'active_record'.camelize # => "ActiveRecord"
-
# 'active_record'.camelize(:lower) # => "activeRecord"
-
# 'active_record/errors'.camelize # => "ActiveRecord::Errors"
-
# 'active_record/errors'.camelize(:lower) # => "activeRecord::Errors"
-
1
def camelize(first_letter = :upper)
-
11
case first_letter
-
when :upper
-
11
ActiveSupport::Inflector.camelize(self, true)
-
when :lower
-
ActiveSupport::Inflector.camelize(self, false)
-
end
-
end
-
1
alias_method :camelcase, :camelize
-
-
# Capitalizes all the words and replaces some characters in the string to create
-
# a nicer looking title. +titleize+ is meant for creating pretty output. It is not
-
# used in the Rails internals.
-
#
-
# +titleize+ is also aliased as +titlecase+.
-
#
-
# 'man from the boondocks'.titleize # => "Man From The Boondocks"
-
# 'x-men: the last stand'.titleize # => "X Men: The Last Stand"
-
1
def titleize
-
ActiveSupport::Inflector.titleize(self)
-
end
-
1
alias_method :titlecase, :titleize
-
-
# The reverse of +camelize+. Makes an underscored, lowercase form from the expression in the string.
-
#
-
# +underscore+ will also change '::' to '/' to convert namespaces to paths.
-
#
-
# 'ActiveModel'.underscore # => "active_model"
-
# 'ActiveModel::Errors'.underscore # => "active_model/errors"
-
1
def underscore
-
104
ActiveSupport::Inflector.underscore(self)
-
end
-
-
# Replaces underscores with dashes in the string.
-
#
-
# 'puni_puni'.dasherize # => "puni-puni"
-
1
def dasherize
-
6
ActiveSupport::Inflector.dasherize(self)
-
end
-
-
# Removes the module part from the constant expression in the string.
-
#
-
# 'ActiveRecord::CoreExtensions::String::Inflections'.demodulize # => "Inflections"
-
# 'Inflections'.demodulize # => "Inflections"
-
#
-
# See also +deconstantize+.
-
1
def demodulize
-
ActiveSupport::Inflector.demodulize(self)
-
end
-
-
# Removes the rightmost segment from the constant expression in the string.
-
#
-
# 'Net::HTTP'.deconstantize # => "Net"
-
# '::Net::HTTP'.deconstantize # => "::Net"
-
# 'String'.deconstantize # => ""
-
# '::String'.deconstantize # => ""
-
# ''.deconstantize # => ""
-
#
-
# See also +demodulize+.
-
1
def deconstantize
-
ActiveSupport::Inflector.deconstantize(self)
-
end
-
-
# Replaces special characters in a string so that it may be used as part of a 'pretty' URL.
-
#
-
# class Person
-
# def to_param
-
# "#{id}-#{name.parameterize}"
-
# end
-
# end
-
#
-
# @person = Person.find(1)
-
# # => #<Person id: 1, name: "Donald E. Knuth">
-
#
-
# <%= link_to(@person.name, person_path) %>
-
# # => <a href="/person/1-donald-e-knuth">Donald E. Knuth</a>
-
1
def parameterize(sep = '-')
-
ActiveSupport::Inflector.parameterize(self, sep)
-
end
-
-
# Creates the name of a table like Rails does for models to table names. This method
-
# uses the +pluralize+ method on the last word in the string.
-
#
-
# 'RawScaledScorer'.tableize # => "raw_scaled_scorers"
-
# 'egg_and_ham'.tableize # => "egg_and_hams"
-
# 'fancyCategory'.tableize # => "fancy_categories"
-
1
def tableize
-
ActiveSupport::Inflector.tableize(self)
-
end
-
-
# Create a class name from a plural table name like Rails does for table names to models.
-
# Note that this returns a string and not a class. (To convert to an actual class
-
# follow +classify+ with +constantize+.)
-
#
-
# 'egg_and_hams'.classify # => "EggAndHam"
-
# 'posts'.classify # => "Post"
-
#
-
# Singular names are not handled correctly.
-
#
-
# 'business'.classify # => "Busines"
-
1
def classify
-
ActiveSupport::Inflector.classify(self)
-
end
-
-
# Capitalizes the first word, turns underscores into spaces, and strips '_id'.
-
# Like +titleize+, this is meant for creating pretty output.
-
#
-
# 'employee_salary' # => "Employee salary"
-
# 'author_id' # => "Author"
-
1
def humanize
-
75
ActiveSupport::Inflector.humanize(self)
-
end
-
-
# Creates a foreign key name from a class name.
-
# +separate_class_name_and_id_with_underscore+ sets whether
-
# the method should put '_' between the name and 'id'.
-
#
-
# 'Message'.foreign_key # => "message_id"
-
# 'Message'.foreign_key(false) # => "messageid"
-
# 'Admin::Post'.foreign_key # => "post_id"
-
1
def foreign_key(separate_class_name_and_id_with_underscore = true)
-
ActiveSupport::Inflector.foreign_key(self, separate_class_name_and_id_with_underscore)
-
end
-
end
-
# encoding: utf-8
-
1
require 'active_support/multibyte'
-
-
1
class String
-
# == Multibyte proxy
-
#
-
# +mb_chars+ is a multibyte safe proxy for string methods.
-
#
-
# In Ruby 1.8 and older it creates and returns an instance of the ActiveSupport::Multibyte::Chars class which
-
# encapsulates the original string. A Unicode safe version of all the String methods are defined on this proxy
-
# class. If the proxy class doesn't respond to a certain method, it's forwarded to the encapsulated string.
-
#
-
# name = 'Claus Müller'
-
# name.reverse # => "rell??M sualC"
-
# name.length # => 13
-
#
-
# name.mb_chars.reverse.to_s # => "rellüM sualC"
-
# name.mb_chars.length # => 12
-
#
-
# In Ruby 1.9 and newer +mb_chars+ returns +self+ because String is (mostly) encoding aware. This means that
-
# it becomes easy to run one version of your code on multiple Ruby versions.
-
#
-
# == Method chaining
-
#
-
# All the methods on the Chars proxy which normally return a string will return a Chars object. This allows
-
# method chaining on the result of any of these methods.
-
#
-
# name.mb_chars.reverse.length # => 12
-
#
-
# == Interoperability and configuration
-
#
-
# The Chars object tries to be as interchangeable with String objects as possible: sorting and comparing between
-
# String and Char work like expected. The bang! methods change the internal string representation in the Chars
-
# object. Interoperability problems can be resolved easily with a +to_s+ call.
-
#
-
# For more information about the methods defined on the Chars proxy see ActiveSupport::Multibyte::Chars. For
-
# information about how to change the default Multibyte behavior see ActiveSupport::Multibyte.
-
1
def mb_chars
-
36
if ActiveSupport::Multibyte.proxy_class.consumes?(self)
-
36
ActiveSupport::Multibyte.proxy_class.new(self)
-
else
-
self
-
end
-
end
-
-
1
def is_utf8?
-
case encoding
-
when Encoding::UTF_8
-
valid_encoding?
-
when Encoding::ASCII_8BIT, Encoding::US_ASCII
-
dup.force_encoding(Encoding::UTF_8).valid_encoding?
-
else
-
false
-
end
-
end
-
end
-
1
require 'erb'
-
1
require 'active_support/core_ext/kernel/singleton_class'
-
-
1
class ERB
-
1
module Util
-
1
HTML_ESCAPE = { '&' => '&', '>' => '>', '<' => '<', '"' => '"', "'" => ''' }
-
1
JSON_ESCAPE = { '&' => '\u0026', '>' => '\u003E', '<' => '\u003C' }
-
1
HTML_ESCAPE_ONCE_REGEXP = /["><']|&(?!([a-zA-Z]+|(#\d+));)/
-
1
JSON_ESCAPE_REGEXP = /[&"><]/
-
-
# A utility method for escaping HTML tag characters.
-
# This method is also aliased as <tt>h</tt>.
-
#
-
# In your ERB templates, use this method to escape any unsafe content. For example:
-
# <%=h @person.name %>
-
#
-
# puts html_escape('is a > 0 & a < 10?')
-
# # => is a > 0 & a < 10?
-
1
def html_escape(s)
-
44
s = s.to_s
-
44
if s.html_safe?
-
s
-
else
-
44
s.gsub(/[&"'><]/, HTML_ESCAPE).html_safe
-
end
-
end
-
-
# Aliasing twice issues a warning "discarding old...". Remove first to avoid it.
-
1
remove_method(:h)
-
1
alias h html_escape
-
-
1
module_function :h
-
-
1
singleton_class.send(:remove_method, :html_escape)
-
1
module_function :html_escape
-
-
# A utility method for escaping HTML without affecting existing escaped entities.
-
#
-
# html_escape_once('1 < 2 & 3')
-
# # => "1 < 2 & 3"
-
#
-
# html_escape_once('<< Accept & Checkout')
-
# # => "<< Accept & Checkout"
-
1
def html_escape_once(s)
-
result = s.to_s.gsub(HTML_ESCAPE_ONCE_REGEXP) { |special| HTML_ESCAPE[special] }
-
s.html_safe? ? result.html_safe : result
-
end
-
-
1
module_function :html_escape_once
-
-
# A utility method for escaping HTML entities in JSON strings
-
# using \uXXXX JavaScript escape sequences for string literals:
-
#
-
# json_escape('is a > 0 & a < 10?')
-
# # => is a \u003E 0 \u0026 a \u003C 10?
-
#
-
# Note that after this operation is performed the output is not
-
# valid JSON. In particular double quotes are removed:
-
#
-
# json_escape('{"name":"john","created_at":"2010-04-28T01:39:31Z","id":1}')
-
# # => {name:john,created_at:2010-04-28T01:39:31Z,id:1}
-
1
def json_escape(s)
-
result = s.to_s.gsub(JSON_ESCAPE_REGEXP) { |special| JSON_ESCAPE[special] }
-
s.html_safe? ? result.html_safe : result
-
end
-
-
1
module_function :json_escape
-
end
-
end
-
-
1
class Object
-
1
def html_safe?
-
73
false
-
end
-
end
-
-
1
class Numeric
-
1
def html_safe?
-
true
-
end
-
end
-
-
1
module ActiveSupport #:nodoc:
-
1
class SafeBuffer < String
-
1
UNSAFE_STRING_METHODS = %w(
-
capitalize chomp chop delete downcase gsub lstrip next reverse rstrip
-
slice squeeze strip sub succ swapcase tr tr_s upcase prepend
-
)
-
-
1
alias_method :original_concat, :concat
-
1
private :original_concat
-
-
1
class SafeConcatError < StandardError
-
1
def initialize
-
super 'Could not concatenate to the buffer because it is not html safe.'
-
end
-
end
-
-
1
def [](*args)
-
if args.size < 2
-
super
-
else
-
if html_safe?
-
new_safe_buffer = super
-
new_safe_buffer.instance_eval { @html_safe = true }
-
new_safe_buffer
-
else
-
to_str[*args]
-
end
-
end
-
end
-
-
1
def safe_concat(value)
-
108
raise SafeConcatError unless html_safe?
-
108
original_concat(value)
-
end
-
-
1
def initialize(*)
-
202
@html_safe = true
-
202
super
-
end
-
-
1
def initialize_copy(other)
-
super
-
@html_safe = other.html_safe?
-
end
-
-
1
def clone_empty
-
self[0, 0]
-
end
-
-
1
def concat(value)
-
46
if !html_safe? || value.html_safe?
-
17
super(value)
-
else
-
29
super(ERB::Util.h(value))
-
end
-
end
-
1
alias << concat
-
-
1
def +(other)
-
dup.concat(other)
-
end
-
-
1
def %(args)
-
args = Array(args).map do |arg|
-
if !html_safe? || arg.html_safe?
-
arg
-
else
-
ERB::Util.h(arg)
-
end
-
end
-
-
self.class.new(super(args))
-
end
-
-
1
def html_safe?
-
171
defined?(@html_safe) && @html_safe
-
end
-
-
1
def to_s
-
251
self
-
end
-
-
1
def to_param
-
to_str
-
end
-
-
1
def encode_with(coder)
-
coder.represent_scalar nil, to_str
-
end
-
-
1
UNSAFE_STRING_METHODS.each do |unsafe_method|
-
20
if 'String'.respond_to?(unsafe_method)
-
20
class_eval <<-EOT, __FILE__, __LINE__ + 1
-
def #{unsafe_method}(*args, &block) # def capitalize(*args, &block)
-
to_str.#{unsafe_method}(*args, &block) # to_str.capitalize(*args, &block)
-
end # end
-
-
def #{unsafe_method}!(*args) # def capitalize!(*args)
-
@html_safe = false # @html_safe = false
-
super # super
-
end # end
-
EOT
-
end
-
end
-
end
-
end
-
-
1
class String
-
1
def html_safe
-
69
ActiveSupport::SafeBuffer.new(self)
-
end
-
end
-
1
class String
-
1
alias_method :starts_with?, :start_with?
-
1
alias_method :ends_with?, :end_with?
-
end
-
1
require 'active_support/core_ext/object/acts_like'
-
-
1
class Time
-
# Duck-types as a Time-like class. See Object#acts_like?.
-
1
def acts_like_time?
-
true
-
end
-
end
-
1
require 'active_support/duration'
-
1
require 'active_support/core_ext/time/conversions'
-
1
require 'active_support/time_with_zone'
-
1
require 'active_support/core_ext/time/zones'
-
1
require 'active_support/core_ext/date_and_time/calculations'
-
-
1
class Time
-
1
include DateAndTime::Calculations
-
-
1
COMMON_YEAR_DAYS_IN_MONTH = [nil, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
-
-
1
class << self
-
# Overriding case equality method so that it returns true for ActiveSupport::TimeWithZone instances
-
1
def ===(other)
-
super || (self == Time && other.is_a?(ActiveSupport::TimeWithZone))
-
end
-
-
# Return the number of days in the given month.
-
# If no year is specified, it will use the current year.
-
1
def days_in_month(month, year = now.year)
-
if month == 2 && ::Date.gregorian_leap?(year)
-
29
-
else
-
COMMON_YEAR_DAYS_IN_MONTH[month]
-
end
-
end
-
-
# Returns a new Time if requested year can be accommodated by Ruby's Time class
-
# (i.e., if year is within either 1970..2038 or 1902..2038, depending on system architecture);
-
# otherwise returns a DateTime.
-
1
def time_with_datetime_fallback(utc_or_local, year, month=1, day=1, hour=0, min=0, sec=0, usec=0)
-
time = ::Time.send(utc_or_local, year, month, day, hour, min, sec, usec)
-
-
# This check is needed because Time.utc(y) returns a time object in the 2000s for 0 <= y <= 138.
-
if time.year == year
-
time
-
else
-
::DateTime.civil_from_format(utc_or_local, year, month, day, hour, min, sec)
-
end
-
rescue
-
::DateTime.civil_from_format(utc_or_local, year, month, day, hour, min, sec)
-
end
-
-
# Wraps class method +time_with_datetime_fallback+ with +utc_or_local+ set to <tt>:utc</tt>.
-
1
def utc_time(*args)
-
time_with_datetime_fallback(:utc, *args)
-
end
-
-
# Wraps class method +time_with_datetime_fallback+ with +utc_or_local+ set to <tt>:local</tt>.
-
1
def local_time(*args)
-
time_with_datetime_fallback(:local, *args)
-
end
-
-
# Returns <tt>Time.zone.now</tt> when <tt>Time.zone</tt> or <tt>config.time_zone</tt> are set, otherwise just returns <tt>Time.now</tt>.
-
1
def current
-
1
::Time.zone ? ::Time.zone.now : ::Time.now
-
end
-
end
-
-
# Seconds since midnight: Time.now.seconds_since_midnight
-
1
def seconds_since_midnight
-
to_i - change(:hour => 0).to_i + (usec / 1.0e+6)
-
end
-
-
# Returns a new Time where one or more of the elements have been changed according
-
# to the +options+ parameter. The time options (<tt>:hour</tt>, <tt>:min</tt>,
-
# <tt>:sec</tt>, <tt>:usec</tt>) reset cascadingly, so if only the hour is passed,
-
# then minute, sec, and usec is set to 0. If the hour and minute is passed, then
-
# sec and usec is set to 0. The +options+ parameter takes a hash with any of these
-
# keys: <tt>:year</tt>, <tt>:month</tt>, <tt>:day</tt>, <tt>:hour</tt>, <tt>:min</tt>,
-
# <tt>:sec</tt>, <tt>:usec</tt>.
-
#
-
# Time.new(2012, 8, 29, 22, 35, 0).change(day: 1) # => Time.new(2012, 8, 1, 22, 35, 0)
-
# Time.new(2012, 8, 29, 22, 35, 0).change(year: 1981, day: 1) # => Time.new(1981, 8, 1, 22, 35, 0)
-
# Time.new(2012, 8, 29, 22, 35, 0).change(year: 1981, hour: 0) # => Time.new(1981, 8, 29, 0, 0, 0)
-
1
def change(options)
-
2
new_year = options.fetch(:year, year)
-
2
new_month = options.fetch(:month, month)
-
2
new_day = options.fetch(:day, day)
-
2
new_hour = options.fetch(:hour, hour)
-
2
new_min = options.fetch(:min, options[:hour] ? 0 : min)
-
2
new_sec = options.fetch(:sec, (options[:hour] || options[:min]) ? 0 : sec)
-
2
new_usec = options.fetch(:usec, (options[:hour] || options[:min] || options[:sec]) ? 0 : Rational(nsec, 1000))
-
-
2
if utc?
-
::Time.utc(new_year, new_month, new_day, new_hour, new_min, new_sec, new_usec)
-
2
elsif zone
-
2
::Time.local(new_year, new_month, new_day, new_hour, new_min, new_sec, new_usec)
-
else
-
::Time.new(new_year, new_month, new_day, new_hour, new_min, new_sec + (new_usec.to_r / 1000000), utc_offset)
-
end
-
end
-
-
# Uses Date to provide precise Time calculations for years, months, and days.
-
# The +options+ parameter takes a hash with any of these keys: <tt>:years</tt>,
-
# <tt>:months</tt>, <tt>:weeks</tt>, <tt>:days</tt>, <tt>:hours</tt>,
-
# <tt>:minutes</tt>, <tt>:seconds</tt>.
-
1
def advance(options)
-
1
unless options[:weeks].nil?
-
options[:weeks], partial_weeks = options[:weeks].divmod(1)
-
options[:days] = options.fetch(:days, 0) + 7 * partial_weeks
-
end
-
-
1
unless options[:days].nil?
-
1
options[:days], partial_days = options[:days].divmod(1)
-
1
options[:hours] = options.fetch(:hours, 0) + 24 * partial_days
-
end
-
-
1
d = to_date.advance(options)
-
1
time_advanced_by_date = change(:year => d.year, :month => d.month, :day => d.day)
-
1
seconds_to_advance = \
-
options.fetch(:seconds, 0) +
-
options.fetch(:minutes, 0) * 60 +
-
options.fetch(:hours, 0) * 3600
-
-
1
if seconds_to_advance.zero?
-
1
time_advanced_by_date
-
else
-
time_advanced_by_date.since(seconds_to_advance)
-
end
-
end
-
-
# Returns a new Time representing the time a number of seconds ago, this is basically a wrapper around the Numeric extension
-
1
def ago(seconds)
-
since(-seconds)
-
end
-
-
# Returns a new Time representing the time a number of seconds since the instance time
-
1
def since(seconds)
-
self + seconds
-
rescue
-
to_datetime.since(seconds)
-
end
-
1
alias :in :since
-
-
# Returns a new Time representing the start of the day (0:00)
-
1
def beginning_of_day
-
#(self - seconds_since_midnight).change(usec: 0)
-
1
change(:hour => 0)
-
end
-
1
alias :midnight :beginning_of_day
-
1
alias :at_midnight :beginning_of_day
-
1
alias :at_beginning_of_day :beginning_of_day
-
-
# Returns a new Time representing the end of the day, 23:59:59.999999 (.999999999 in ruby1.9)
-
1
def end_of_day
-
change(
-
:hour => 23,
-
:min => 59,
-
:sec => 59,
-
:usec => Rational(999999999, 1000)
-
)
-
end
-
-
# Returns a new Time representing the start of the hour (x:00)
-
1
def beginning_of_hour
-
change(:min => 0)
-
end
-
1
alias :at_beginning_of_hour :beginning_of_hour
-
-
# Returns a new Time representing the end of the hour, x:59:59.999999 (.999999999 in ruby1.9)
-
1
def end_of_hour
-
change(
-
:min => 59,
-
:sec => 59,
-
:usec => Rational(999999999, 1000)
-
)
-
end
-
-
# Returns a Range representing the whole day of the current time.
-
1
def all_day
-
beginning_of_day..end_of_day
-
end
-
-
# Returns a Range representing the whole week of the current time.
-
# Week starts on start_day, default is <tt>Date.week_start</tt> or <tt>config.week_start</tt> when set.
-
1
def all_week(start_day = Date.beginning_of_week)
-
beginning_of_week(start_day)..end_of_week(start_day)
-
end
-
-
# Returns a Range representing the whole month of the current time.
-
1
def all_month
-
beginning_of_month..end_of_month
-
end
-
-
# Returns a Range representing the whole quarter of the current time.
-
1
def all_quarter
-
beginning_of_quarter..end_of_quarter
-
end
-
-
# Returns a Range representing the whole year of the current time.
-
1
def all_year
-
beginning_of_year..end_of_year
-
end
-
-
1
def plus_with_duration(other) #:nodoc:
-
if ActiveSupport::Duration === other
-
other.since(self)
-
else
-
plus_without_duration(other)
-
end
-
end
-
1
alias_method :plus_without_duration, :+
-
1
alias_method :+, :plus_with_duration
-
-
1
def minus_with_duration(other) #:nodoc:
-
150
if ActiveSupport::Duration === other
-
other.until(self)
-
else
-
150
minus_without_duration(other)
-
end
-
end
-
1
alias_method :minus_without_duration, :-
-
1
alias_method :-, :minus_with_duration
-
-
# Time#- can also be used to determine the number of seconds between two Time instances.
-
# We're layering on additional behavior so that ActiveSupport::TimeWithZone instances
-
# are coerced into values that Time#- will recognize
-
1
def minus_with_coercion(other)
-
150
other = other.comparable_time if other.respond_to?(:comparable_time)
-
150
other.is_a?(DateTime) ? to_f - other.to_f : minus_without_coercion(other)
-
end
-
1
alias_method :minus_without_coercion, :-
-
1
alias_method :-, :minus_with_coercion
-
-
# Layers additional behavior on Time#<=> so that DateTime and ActiveSupport::TimeWithZone instances
-
# can be chronologically compared with a Time
-
1
def compare_with_coercion(other)
-
# we're avoiding Time#to_datetime cause it's expensive
-
if other.is_a?(Time)
-
compare_without_coercion(other.to_time)
-
else
-
to_datetime <=> other
-
end
-
end
-
1
alias_method :compare_without_coercion, :<=>
-
1
alias_method :<=>, :compare_with_coercion
-
-
# Layers additional behavior on Time#eql? so that ActiveSupport::TimeWithZone instances
-
# can be eql? to an equivalent Time
-
1
def eql_with_coercion(other)
-
# if other is an ActiveSupport::TimeWithZone, coerce a Time instance from it so we can do eql? comparison
-
other = other.comparable_time if other.respond_to?(:comparable_time)
-
eql_without_coercion(other)
-
end
-
1
alias_method :eql_without_coercion, :eql?
-
1
alias_method :eql?, :eql_with_coercion
-
-
end
-
1
require 'active_support/inflector/methods'
-
1
require 'active_support/values/time_zone'
-
-
1
class Time
-
1
DATE_FORMATS = {
-
:db => '%Y-%m-%d %H:%M:%S',
-
:number => '%Y%m%d%H%M%S',
-
:nsec => '%Y%m%d%H%M%S%9N',
-
:time => '%H:%M',
-
:short => '%d %b %H:%M',
-
:long => '%B %d, %Y %H:%M',
-
:long_ordinal => lambda { |time|
-
day_format = ActiveSupport::Inflector.ordinalize(time.day)
-
time.strftime("%B #{day_format}, %Y %H:%M")
-
},
-
:rfc822 => lambda { |time|
-
offset_format = time.formatted_offset(false)
-
time.strftime("%a, %d %b %Y %H:%M:%S #{offset_format}")
-
}
-
}
-
-
# Converts to a formatted string. See DATE_FORMATS for builtin formats.
-
#
-
# This method is aliased to <tt>to_s</tt>.
-
#
-
# time = Time.now # => Thu Jan 18 06:10:17 CST 2007
-
#
-
# time.to_formatted_s(:time) # => "06:10"
-
# time.to_s(:time) # => "06:10"
-
#
-
# time.to_formatted_s(:db) # => "2007-01-18 06:10:17"
-
# time.to_formatted_s(:number) # => "20070118061017"
-
# time.to_formatted_s(:short) # => "18 Jan 06:10"
-
# time.to_formatted_s(:long) # => "January 18, 2007 06:10"
-
# time.to_formatted_s(:long_ordinal) # => "January 18th, 2007 06:10"
-
# time.to_formatted_s(:rfc822) # => "Thu, 18 Jan 2007 06:10:17 -0600"
-
#
-
# == Adding your own time formats to +to_formatted_s+
-
# You can add your own formats to the Time::DATE_FORMATS hash.
-
# Use the format name as the hash key and either a strftime string
-
# or Proc instance that takes a time argument as the value.
-
#
-
# # config/initializers/time_formats.rb
-
# Time::DATE_FORMATS[:month_and_year] = '%B %Y'
-
# Time::DATE_FORMATS[:short_ordinal] = ->(time) { time.strftime("%B #{time.day.ordinalize}") }
-
1
def to_formatted_s(format = :default)
-
4
if formatter = DATE_FORMATS[format]
-
formatter.respond_to?(:call) ? formatter.call(self).to_s : strftime(formatter)
-
else
-
4
to_default_s
-
end
-
end
-
1
alias_method :to_default_s, :to_s
-
1
alias_method :to_s, :to_formatted_s
-
-
# Returns the UTC offset as an +HH:MM formatted string.
-
#
-
# Time.local(2000).formatted_offset # => "-06:00"
-
# Time.local(2000).formatted_offset(false) # => "-0600"
-
1
def formatted_offset(colon = true, alternate_utc_string = nil)
-
utc? && alternate_utc_string || ActiveSupport::TimeZone.seconds_to_utc_offset(utc_offset, colon)
-
end
-
end
-
# Ruby 1.9.2 adds utc_offset and zone to Time, but marshaling only
-
# preserves utc_offset. Preserve zone also, even though it may not
-
# work in some edge cases.
-
1
if Time.local(2010).zone != Marshal.load(Marshal.dump(Time.local(2010))).zone
-
1
class Time
-
1
class << self
-
1
alias_method :_load_without_zone, :_load
-
1
def _load(marshaled_time)
-
time = _load_without_zone(marshaled_time)
-
time.instance_eval do
-
if zone = defined?(@_zone) && remove_instance_variable('@_zone')
-
ary = to_a
-
ary[0] += subsec if ary[0] == sec
-
ary[-1] = zone
-
utc? ? Time.utc(*ary) : Time.local(*ary)
-
else
-
self
-
end
-
end
-
end
-
end
-
-
1
alias_method :_dump_without_zone, :_dump
-
1
def _dump(*args)
-
obj = dup
-
obj.instance_variable_set('@_zone', zone)
-
obj._dump_without_zone(*args)
-
end
-
end
-
end
-
1
require 'active_support/time_with_zone'
-
-
1
class Time
-
1
@zone_default = nil
-
-
1
class << self
-
1
attr_accessor :zone_default
-
-
# Returns the TimeZone for the current request, if this has been set (via Time.zone=).
-
# If <tt>Time.zone</tt> has not been set for the current request, returns the TimeZone specified in <tt>config.time_zone</tt>.
-
1
def zone
-
1
Thread.current[:time_zone] || zone_default
-
end
-
-
# Sets <tt>Time.zone</tt> to a TimeZone object for the current request/thread.
-
#
-
# This method accepts any of the following:
-
#
-
# * A Rails TimeZone object.
-
# * An identifier for a Rails TimeZone object (e.g., "Eastern Time (US & Canada)", <tt>-5.hours</tt>).
-
# * A TZInfo::Timezone object.
-
# * An identifier for a TZInfo::Timezone object (e.g., "America/New_York").
-
#
-
# Here's an example of how you might set <tt>Time.zone</tt> on a per request basis and reset it when the request is done.
-
# <tt>current_user.time_zone</tt> just needs to return a string identifying the user's preferred time zone:
-
#
-
# class ApplicationController < ActionController::Base
-
# around_filter :set_time_zone
-
#
-
# def set_time_zone
-
# if logged_in?
-
# Time.use_zone(current_user.time_zone) { yield }
-
# else
-
# yield
-
# end
-
# end
-
# end
-
1
def zone=(time_zone)
-
Thread.current[:time_zone] = find_zone!(time_zone)
-
end
-
-
# Allows override of <tt>Time.zone</tt> locally inside supplied block; resets <tt>Time.zone</tt> to existing value when done.
-
1
def use_zone(time_zone)
-
new_zone = find_zone!(time_zone)
-
begin
-
old_zone, ::Time.zone = ::Time.zone, new_zone
-
yield
-
ensure
-
::Time.zone = old_zone
-
end
-
end
-
-
# Returns a TimeZone instance or nil, or raises an ArgumentError for invalid timezones.
-
1
def find_zone!(time_zone)
-
if !time_zone || time_zone.is_a?(ActiveSupport::TimeZone)
-
time_zone
-
else
-
# lookup timezone based on identifier (unless we've been passed a TZInfo::Timezone)
-
unless time_zone.respond_to?(:period_for_local)
-
time_zone = ActiveSupport::TimeZone[time_zone] || TZInfo::Timezone.get(time_zone)
-
end
-
-
# Return if a TimeZone instance, or wrap in a TimeZone instance if a TZInfo::Timezone
-
if time_zone.is_a?(ActiveSupport::TimeZone)
-
time_zone
-
else
-
ActiveSupport::TimeZone.create(time_zone.name, nil, time_zone)
-
end
-
end
-
rescue TZInfo::InvalidTimezoneIdentifier
-
raise ArgumentError, "Invalid Timezone: #{time_zone}"
-
end
-
-
1
def find_zone(time_zone)
-
find_zone!(time_zone) rescue nil
-
end
-
end
-
-
# Returns the simultaneous time in <tt>Time.zone</tt>.
-
#
-
# Time.zone = 'Hawaii' # => 'Hawaii'
-
# Time.utc(2000).in_time_zone # => Fri, 31 Dec 1999 14:00:00 HST -10:00
-
#
-
# This method is similar to Time#localtime, except that it uses <tt>Time.zone</tt> as the local zone
-
# instead of the operating system's time zone.
-
#
-
# You can also pass in a TimeZone instance or string that identifies a TimeZone as an argument,
-
# and the conversion will be based on that zone instead of <tt>Time.zone</tt>.
-
#
-
# Time.utc(2000).in_time_zone('Alaska') # => Fri, 31 Dec 1999 15:00:00 AKST -09:00
-
1
def in_time_zone(zone = ::Time.zone)
-
if zone
-
ActiveSupport::TimeWithZone.new(utc? ? self : getutc, ::Time.find_zone!(zone))
-
else
-
self
-
end
-
end
-
end
-
# encoding: utf-8
-
-
1
require 'uri'
-
1
str = "\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E" # Ni-ho-nn-go in UTF-8, means Japanese.
-
1
parser = URI::Parser.new
-
-
1
unless str == parser.unescape(parser.escape(str))
-
1
URI::Parser.class_eval do
-
1
remove_method :unescape
-
1
def unescape(str, escaped = /%[a-fA-F\d]{2}/)
-
# TODO: Are we actually sure that ASCII == UTF-8?
-
# YK: My initial experiments say yes, but let's be sure please
-
2
enc = str.encoding
-
2
enc = Encoding::UTF_8 if enc == Encoding::US_ASCII
-
2
str.gsub(escaped) { [$&[1, 2].hex].pack('C') }.force_encoding(enc)
-
end
-
end
-
end
-
-
1
module URI
-
1
class << self
-
1
def parser
-
3
@parser ||= URI::Parser.new
-
end
-
end
-
end
-
1
require 'set'
-
1
require 'thread'
-
1
require 'pathname'
-
1
require 'active_support/core_ext/module/aliasing'
-
1
require 'active_support/core_ext/module/attribute_accessors'
-
1
require 'active_support/core_ext/module/introspection'
-
1
require 'active_support/core_ext/module/anonymous'
-
1
require 'active_support/core_ext/module/qualified_const'
-
1
require 'active_support/core_ext/object/blank'
-
1
require 'active_support/core_ext/load_error'
-
1
require 'active_support/core_ext/name_error'
-
1
require 'active_support/core_ext/string/starts_ends_with'
-
1
require 'active_support/inflector'
-
-
1
module ActiveSupport #:nodoc:
-
1
module Dependencies #:nodoc:
-
1
extend self
-
-
# Should we turn on Ruby warnings on the first load of dependent files?
-
1
mattr_accessor :warnings_on_first_load
-
1
self.warnings_on_first_load = false
-
-
# All files ever loaded.
-
1
mattr_accessor :history
-
1
self.history = Set.new
-
-
# All files currently loaded.
-
1
mattr_accessor :loaded
-
1
self.loaded = Set.new
-
-
# Should we load files or require them?
-
1
mattr_accessor :mechanism
-
1
self.mechanism = ENV['NO_RELOAD'] ? :require : :load
-
-
# The set of directories from which we may automatically load files. Files
-
# under these directories will be reloaded on each request in development mode,
-
# unless the directory also appears in autoload_once_paths.
-
1
mattr_accessor :autoload_paths
-
1
self.autoload_paths = []
-
-
# The set of directories from which automatically loaded constants are loaded
-
# only once. All directories in this set must also be present in +autoload_paths+.
-
1
mattr_accessor :autoload_once_paths
-
1
self.autoload_once_paths = []
-
-
# An array of qualified constant names that have been loaded. Adding a name
-
# to this array will cause it to be unloaded the next time Dependencies are
-
# cleared.
-
1
mattr_accessor :autoloaded_constants
-
1
self.autoloaded_constants = []
-
-
# An array of constant names that need to be unloaded on every request. Used
-
# to allow arbitrary constants to be marked for unloading.
-
1
mattr_accessor :explicitly_unloadable_constants
-
1
self.explicitly_unloadable_constants = []
-
-
# The logger is used for generating information on the action run-time
-
# (including benchmarking) if available. Can be set to nil for no logging.
-
# Compatible with both Ruby's own Logger and Log4r loggers.
-
1
mattr_accessor :logger
-
-
# Set to +true+ to enable logging of const_missing and file loads.
-
1
mattr_accessor :log_activity
-
1
self.log_activity = false
-
-
# The WatchStack keeps a stack of the modules being watched as files are
-
# loaded. If a file in the process of being loaded (parent.rb) triggers the
-
# load of another file (child.rb) the stack will ensure that child.rb
-
# handles the new constants.
-
#
-
# If child.rb is being autoloaded, its constants will be added to
-
# autoloaded_constants. If it was being `require`d, they will be discarded.
-
#
-
# This is handled by walking back up the watch stack and adding the constants
-
# found by child.rb to the list of original constants in parent.rb.
-
1
class WatchStack
-
1
include Enumerable
-
-
# @watching is a stack of lists of constants being watched. For instance,
-
# if parent.rb is autoloaded, the stack will look like [[Object]]. If
-
# parent.rb then requires namespace/child.rb, the stack will look like
-
# [[Object], [Namespace]].
-
-
1
def initialize
-
1
@watching = []
-
1
@stack = Hash.new { |h,k| h[k] = [] }
-
end
-
-
1
def each(&block)
-
@stack.each(&block)
-
end
-
-
1
def watching?
-
350
!@watching.empty?
-
end
-
-
# Returns a list of new constants found since the last call to
-
# <tt>watch_namespaces</tt>.
-
1
def new_constants
-
23
constants = []
-
-
# Grab the list of namespaces that we're looking for new constants under
-
23
@watching.last.each do |namespace|
-
# Retrieve the constants that were present under the namespace when watch_namespaces
-
# was originally called
-
original_constants = @stack[namespace].last
-
-
mod = Inflector.constantize(namespace) if Dependencies.qualified_const_defined?(namespace)
-
next unless mod.is_a?(Module)
-
-
# Get a list of the constants that were added
-
new_constants = mod.local_constants - original_constants
-
-
# self[namespace] returns an Array of the constants that are being evaluated
-
# for that namespace. For instance, if parent.rb requires child.rb, the first
-
# element of self[Object] will be an Array of the constants that were present
-
# before parent.rb was required. The second element will be an Array of the
-
# constants that were present before child.rb was required.
-
@stack[namespace].each do |namespace_constants|
-
namespace_constants.concat(new_constants)
-
end
-
-
# Normalize the list of new constants, and add them to the list we will return
-
new_constants.each do |suffix|
-
constants << ([namespace, suffix] - ["Object"]).join("::")
-
end
-
end
-
23
constants
-
ensure
-
# A call to new_constants is always called after a call to watch_namespaces
-
23
pop_modules(@watching.pop)
-
end
-
-
# Add a set of modules to the watch stack, remembering the initial
-
# constants.
-
1
def watch_namespaces(namespaces)
-
@watching << namespaces.map do |namespace|
-
module_name = Dependencies.to_constant_name(namespace)
-
original_constants = Dependencies.qualified_const_defined?(module_name) ?
-
Inflector.constantize(module_name).local_constants : []
-
-
@stack[module_name] << original_constants
-
module_name
-
23
end
-
end
-
-
1
private
-
1
def pop_modules(modules)
-
23
modules.each { |mod| @stack[mod].pop }
-
end
-
end
-
-
# An internal stack used to record which constants are loaded by any block.
-
1
mattr_accessor :constant_watch_stack
-
1
self.constant_watch_stack = WatchStack.new
-
-
# Module includes this module.
-
1
module ModuleConstMissing #:nodoc:
-
1
def self.append_features(base)
-
1
base.class_eval do
-
# Emulate #exclude via an ivar
-
1
return if defined?(@_const_missing) && @_const_missing
-
1
@_const_missing = instance_method(:const_missing)
-
1
remove_method(:const_missing)
-
end
-
1
super
-
end
-
-
1
def self.exclude_from(base)
-
base.class_eval do
-
define_method :const_missing, @_const_missing
-
@_const_missing = nil
-
end
-
end
-
-
1
def const_missing(const_name)
-
# The interpreter does not pass nesting information, and in the
-
# case of anonymous modules we cannot even make the trade-off of
-
# assuming their name reflects the nesting. Resort to Object as
-
# the only meaningful guess we can make.
-
2
from_mod = anonymous? ? ::Object : self
-
2
Dependencies.load_missing_constant(from_mod, const_name)
-
end
-
-
1
def unloadable(const_desc = self)
-
super(const_desc)
-
end
-
end
-
-
# Object includes this module.
-
1
module Loadable #:nodoc:
-
1
def self.exclude_from(base)
-
base.class_eval { define_method(:load, Kernel.instance_method(:load)) }
-
end
-
-
1
def require_or_load(file_name)
-
Dependencies.require_or_load(file_name)
-
end
-
-
1
def require_dependency(file_name, message = "No such file to load -- %s")
-
23
unless file_name.is_a?(String)
-
raise ArgumentError, "the file name must be a String -- you passed #{file_name.inspect}"
-
end
-
-
23
Dependencies.depend_on(file_name, message)
-
end
-
-
1
def load_dependency(file)
-
350
if Dependencies.load? && ActiveSupport::Dependencies.constant_watch_stack.watching?
-
Dependencies.new_constants_in(Object) { yield }
-
else
-
350
yield
-
end
-
rescue Exception => exception # errors from loading file
-
exception.blame_file! file
-
raise
-
end
-
-
1
def load(file, wrap = false)
-
result = false
-
load_dependency(file) { result = super }
-
result
-
end
-
-
1
def require(file)
-
350
result = false
-
700
load_dependency(file) { result = super }
-
350
result
-
end
-
-
# Mark the given constant as unloadable. Unloadable constants are removed
-
# each time dependencies are cleared.
-
#
-
# Note that marking a constant for unloading need only be done once. Setup
-
# or init scripts may list each unloadable constant that may need unloading;
-
# each constant will be removed for every subsequent clear, as opposed to
-
# for the first clear.
-
#
-
# The provided constant descriptor may be a (non-anonymous) module or class,
-
# or a qualified constant name as a string or symbol.
-
#
-
# Returns +true+ if the constant was not previously marked for unloading,
-
# +false+ otherwise.
-
1
def unloadable(const_desc)
-
Dependencies.mark_for_unload const_desc
-
end
-
end
-
-
# Exception file-blaming.
-
1
module Blamable #:nodoc:
-
1
def blame_file!(file)
-
(@blamed_files ||= []).unshift file
-
end
-
-
1
def blamed_files
-
23
@blamed_files ||= []
-
end
-
-
1
def describe_blame
-
return nil if blamed_files.empty?
-
"This error occurred while loading the following files:\n #{blamed_files.join "\n "}"
-
end
-
-
1
def copy_blame!(exc)
-
23
@blamed_files = exc.blamed_files.clone
-
23
self
-
end
-
end
-
-
1
def hook!
-
2
Object.class_eval { include Loadable }
-
2
Module.class_eval { include ModuleConstMissing }
-
2
Exception.class_eval { include Blamable }
-
end
-
-
1
def unhook!
-
ModuleConstMissing.exclude_from(Module)
-
Loadable.exclude_from(Object)
-
end
-
-
1
def load?
-
373
mechanism == :load
-
end
-
-
1
def depend_on(file_name, message = "No such file to load -- %s.rb")
-
23
path = search_for_file(file_name)
-
23
require_or_load(path || file_name)
-
rescue LoadError => load_error
-
23
if file_name = load_error.message[/ -- (.*?)(\.rb)?$/, 1]
-
23
load_error.message.replace(message % file_name)
-
23
load_error.copy_blame!(load_error)
-
end
-
23
raise
-
end
-
-
1
def clear
-
log_call
-
loaded.clear
-
remove_unloadable_constants!
-
end
-
-
1
def require_or_load(file_name, const_path = nil)
-
23
log_call file_name, const_path
-
23
file_name = $` if file_name =~ /\.rb\z/
-
23
expanded = File.expand_path(file_name)
-
23
return if loaded.include?(expanded)
-
-
# Record that we've seen this file *before* loading it to avoid an
-
# infinite loop with mutual dependencies.
-
23
loaded << expanded
-
-
23
begin
-
23
if load?
-
23
log "loading #{file_name}"
-
-
# Enable warnings if this file has not been loaded before and
-
# warnings_on_first_load is set.
-
23
load_args = ["#{file_name}.rb"]
-
23
load_args << const_path unless const_path.nil?
-
-
23
if !warnings_on_first_load or history.include?(expanded)
-
23
result = load_file(*load_args)
-
else
-
enable_warnings { result = load_file(*load_args) }
-
end
-
else
-
log "requiring #{file_name}"
-
result = require file_name
-
end
-
rescue Exception
-
23
loaded.delete expanded
-
23
raise
-
end
-
-
# Record history *after* loading so first load gets warnings.
-
history << expanded
-
result
-
end
-
-
# Is the provided constant path defined?
-
1
def qualified_const_defined?(path)
-
2
Object.qualified_const_defined?(path.sub(/^::/, ''), false)
-
end
-
-
# Given +path+, a filesystem path to a ruby file, return an array of
-
# constant paths which would cause Dependencies to attempt to load this
-
# file.
-
1
def loadable_constants_for_path(path, bases = autoload_paths)
-
23
path = $` if path =~ /\.rb\z/
-
23
expanded_path = File.expand_path(path)
-
23
paths = []
-
-
23
bases.each do |root|
-
expanded_root = File.expand_path(root)
-
next unless %r{\A#{Regexp.escape(expanded_root)}(/|\\)} =~ expanded_path
-
-
nesting = expanded_path[(expanded_root.size)..-1]
-
nesting = nesting[1..-1] if nesting && nesting[0] == ?/
-
next if nesting.blank?
-
-
paths << nesting.camelize
-
end
-
-
23
paths.uniq!
-
23
paths
-
end
-
-
# Search for a file in autoload_paths matching the provided suffix.
-
1
def search_for_file(path_suffix)
-
25
path_suffix = path_suffix.sub(/(\.rb)?$/, ".rb")
-
-
25
autoload_paths.each do |root|
-
path = File.join(root, path_suffix)
-
return path if File.file? path
-
end
-
nil # Gee, I sure wish we had first_match ;-)
-
end
-
-
# Does the provided path_suffix correspond to an autoloadable module?
-
# Instead of returning a boolean, the autoload base for this module is
-
# returned.
-
1
def autoloadable_module?(path_suffix)
-
2
autoload_paths.each do |load_path|
-
return load_path if File.directory? File.join(load_path, path_suffix)
-
end
-
nil
-
end
-
-
1
def load_once_path?(path)
-
# to_s works around a ruby1.9 issue where #starts_with?(Pathname) will always return false
-
autoload_once_paths.any? { |base| path.starts_with? base.to_s }
-
end
-
-
# Attempt to autoload the provided module name by searching for a directory
-
# matching the expected path suffix. If found, the module is created and
-
# assigned to +into+'s constants with the name +const_name+. Provided that
-
# the directory was loaded from a reloadable base path, it is added to the
-
# set of constants that are to be unloaded.
-
1
def autoload_module!(into, const_name, qualified_name, path_suffix)
-
2
return nil unless base_path = autoloadable_module?(path_suffix)
-
mod = Module.new
-
into.const_set const_name, mod
-
autoloaded_constants << qualified_name unless autoload_once_paths.include?(base_path)
-
mod
-
end
-
-
# Load the file at the provided path. +const_paths+ is a set of qualified
-
# constant names. When loading the file, Dependencies will watch for the
-
# addition of these constants. Each that is defined will be marked as
-
# autoloaded, and will be removed when Dependencies.clear is next called.
-
#
-
# If the second parameter is left off, then Dependencies will construct a
-
# set of names that the file at +path+ may define. See
-
# +loadable_constants_for_path+ for more details.
-
1
def load_file(path, const_paths = loadable_constants_for_path(path))
-
23
log_call path, const_paths
-
23
const_paths = [const_paths].compact unless const_paths.is_a? Array
-
23
parent_paths = const_paths.collect { |const_path| const_path[/.*(?=::)/] || :Object }
-
-
23
result = nil
-
23
newly_defined_paths = new_constants_in(*parent_paths) do
-
23
result = Kernel.load path
-
end
-
-
autoloaded_constants.concat newly_defined_paths unless load_once_path?(path)
-
autoloaded_constants.uniq!
-
log "loading #{path} defined #{newly_defined_paths * ', '}" unless newly_defined_paths.empty?
-
result
-
end
-
-
# Returns the constant path for the provided parent and constant name.
-
1
def qualified_name_for(mod, name)
-
2
mod_name = to_constant_name mod
-
2
mod_name == "Object" ? name.to_s : "#{mod_name}::#{name}"
-
end
-
-
# Load the constant named +const_name+ which is missing from +from_mod+. If
-
# it is not possible to load the constant into from_mod, try its parent
-
# module using +const_missing+.
-
1
def load_missing_constant(from_mod, const_name)
-
2
log_call from_mod, const_name
-
-
2
unless qualified_const_defined?(from_mod.name) && Inflector.constantize(from_mod.name).equal?(from_mod)
-
raise ArgumentError, "A copy of #{from_mod} has been removed from the module tree but is still active!"
-
end
-
-
2
raise NameError, "#{from_mod} is not missing constant #{const_name}!" if from_mod.const_defined?(const_name, false)
-
-
2
qualified_name = qualified_name_for from_mod, const_name
-
2
path_suffix = qualified_name.underscore
-
-
2
file_path = search_for_file(path_suffix)
-
-
2
if file_path
-
expanded = File.expand_path(file_path)
-
expanded.sub!(/\.rb\z/, '')
-
-
if loaded.include?(expanded)
-
raise "Circular dependency detected while autoloading constant #{qualified_name}"
-
else
-
require_or_load(expanded)
-
raise LoadError, "Unable to autoload constant #{qualified_name}, expected #{file_path} to define it" unless from_mod.const_defined?(const_name, false)
-
return from_mod.const_get(const_name)
-
end
-
elsif mod = autoload_module!(from_mod, const_name, qualified_name, path_suffix)
-
return mod
-
2
elsif (parent = from_mod.parent) && parent != from_mod &&
-
! from_mod.parents.any? { |p| p.const_defined?(const_name, false) }
-
# If our parents do not have a constant named +const_name+ then we are free
-
# to attempt to load upwards. If they do have such a constant, then this
-
# const_missing must be due to from_mod::const_name, which should not
-
# return constants from from_mod's parents.
-
begin
-
# Since Ruby does not pass the nesting at the point the unknown
-
# constant triggered the callback we cannot fully emulate constant
-
# name lookup and need to make a trade-off: we are going to assume
-
# that the nesting in the body of Foo::Bar is [Foo::Bar, Foo] even
-
# though it might not be. Counterexamples are
-
#
-
# class Foo::Bar
-
# Module.nesting # => [Foo::Bar]
-
# end
-
#
-
# or
-
#
-
# module M::N
-
# module S::T
-
# Module.nesting # => [S::T, M::N]
-
# end
-
# end
-
#
-
# for example.
-
return parent.const_missing(const_name)
-
rescue NameError => e
-
raise unless e.missing_name? qualified_name_for(parent, const_name)
-
end
-
end
-
-
2
raise NameError,
-
"uninitialized constant #{qualified_name}",
-
51
caller.reject { |l| l.starts_with? __FILE__ }
-
end
-
-
# Remove the constants that have been autoloaded, and those that have been
-
# marked for unloading. Before each constant is removed a callback is sent
-
# to its class/module if it implements +before_remove_const+.
-
#
-
# The callback implementation should be restricted to cleaning up caches, etc.
-
# as the environment will be in an inconsistent state, e.g. other constants
-
# may have already been unloaded and not accessible.
-
1
def remove_unloadable_constants!
-
autoloaded_constants.each { |const| remove_constant const }
-
autoloaded_constants.clear
-
Reference.clear!
-
explicitly_unloadable_constants.each { |const| remove_constant const }
-
end
-
-
1
class ClassCache
-
1
def initialize
-
1
@store = Hash.new
-
end
-
-
1
def empty?
-
@store.empty?
-
end
-
-
1
def key?(key)
-
@store.key?(key)
-
end
-
-
1
def get(key)
-
1
key = key.name if key.respond_to?(:name)
-
1
@store[key] ||= Inflector.constantize(key)
-
end
-
1
alias :[] :get
-
-
1
def safe_get(key)
-
key = key.name if key.respond_to?(:name)
-
@store[key] ||= Inflector.safe_constantize(key)
-
end
-
-
1
def store(klass)
-
1
return self unless klass.respond_to?(:name)
-
raise(ArgumentError, 'anonymous classes cannot be cached') if klass.name.empty?
-
@store[klass.name] = klass
-
self
-
end
-
-
1
def clear!
-
@store.clear
-
end
-
end
-
-
1
Reference = ClassCache.new
-
-
# Store a reference to a class +klass+.
-
1
def reference(klass)
-
1
Reference.store klass
-
end
-
-
# Get the reference for class named +name+.
-
# Raises an exception if referenced class does not exist.
-
1
def constantize(name)
-
Reference.get(name)
-
end
-
-
# Get the reference for class named +name+ if one exists.
-
# Otherwise returns +nil+.
-
1
def safe_constantize(name)
-
Reference.safe_get(name)
-
end
-
-
# Determine if the given constant has been automatically loaded.
-
1
def autoloaded?(desc)
-
# No name => anonymous module.
-
return false if desc.is_a?(Module) && desc.anonymous?
-
name = to_constant_name desc
-
return false unless qualified_const_defined? name
-
return autoloaded_constants.include?(name)
-
end
-
-
# Will the provided constant descriptor be unloaded?
-
1
def will_unload?(const_desc)
-
autoloaded?(const_desc) ||
-
explicitly_unloadable_constants.include?(to_constant_name(const_desc))
-
end
-
-
# Mark the provided constant name for unloading. This constant will be
-
# unloaded on each request, not just the next one.
-
1
def mark_for_unload(const_desc)
-
name = to_constant_name const_desc
-
if explicitly_unloadable_constants.include? name
-
false
-
else
-
explicitly_unloadable_constants << name
-
true
-
end
-
end
-
-
# Run the provided block and detect the new constants that were loaded during
-
# its execution. Constants may only be regarded as 'new' once -- so if the
-
# block calls +new_constants_in+ again, then the constants defined within the
-
# inner call will not be reported in this one.
-
#
-
# If the provided block does not run to completion, and instead raises an
-
# exception, any new constants are regarded as being only partially defined
-
# and will be removed immediately.
-
1
def new_constants_in(*descs)
-
23
log_call(*descs)
-
-
23
constant_watch_stack.watch_namespaces(descs)
-
23
aborting = true
-
-
23
begin
-
23
yield # Now yield to the code that is to define new constants.
-
aborting = false
-
ensure
-
23
new_constants = constant_watch_stack.new_constants
-
-
23
log "New constants: #{new_constants * ', '}"
-
23
return new_constants unless aborting
-
-
23
log "Error during loading, removing partially loaded constants "
-
23
new_constants.each { |c| remove_constant(c) }.clear
-
end
-
-
[]
-
end
-
-
# Convert the provided const desc to a qualified constant name (as a string).
-
# A module, class, symbol, or string may be provided.
-
1
def to_constant_name(desc) #:nodoc:
-
2
case desc
-
when String then desc.sub(/^::/, '')
-
when Symbol then desc.to_s
-
when Module
-
desc.name.presence ||
-
2
raise(ArgumentError, "Anonymous modules have no name to be referenced by")
-
else raise TypeError, "Not a valid constant descriptor: #{desc.inspect}"
-
end
-
end
-
-
1
def remove_constant(const) #:nodoc:
-
return false unless qualified_const_defined? const
-
-
# Normalize ::Foo, Foo, Object::Foo, and ::Object::Foo to Object::Foo
-
names = const.to_s.sub(/^::(Object)?/, 'Object::').split("::")
-
to_remove = names.pop
-
parent = Inflector.constantize(names * '::')
-
-
log "removing constant #{const}"
-
constantized = constantize(const)
-
constantized.before_remove_const if constantized.respond_to?(:before_remove_const)
-
parent.instance_eval { remove_const to_remove }
-
-
true
-
end
-
-
1
protected
-
1
def log_call(*args)
-
71
if log_activity?
-
arg_str = args.collect { |arg| arg.inspect } * ', '
-
/in `([a-z_\?\!]+)'/ =~ caller(1).first
-
selector = $1 || '<unknown>'
-
log "called #{selector}(#{arg_str})"
-
end
-
end
-
-
1
def log(msg)
-
69
logger.debug "Dependencies: #{msg}" if log_activity?
-
end
-
-
1
def log_activity?
-
140
logger && log_activity
-
end
-
end
-
end
-
-
1
ActiveSupport::Dependencies.hook!
-
1
require "active_support/inflector/methods"
-
-
1
module ActiveSupport
-
# Autoload and eager load conveniences for your library.
-
#
-
# This module allows you to define autoloads based on
-
# Rails conventions (i.e. no need to define the path
-
# it is automatically guessed based on the filename)
-
# and also define a set of constants that needs to be
-
# eager loaded:
-
#
-
# module MyLib
-
# extend ActiveSupport::Autoload
-
#
-
# autoload :Model
-
#
-
# eager_autoload do
-
# autoload :Cache
-
# end
-
# end
-
#
-
# Then your library can be eager loaded by simply calling:
-
#
-
# MyLib.eager_load!
-
1
module Autoload
-
1
def self.extended(base) # :nodoc:
-
12
base.class_eval do
-
12
@_autoloads = {}
-
12
@_under_path = nil
-
12
@_at_path = nil
-
12
@_eager_autoload = false
-
end
-
end
-
-
1
def autoload(const_name, path = @_at_path)
-
225
unless path
-
181
full = [name, @_under_path, const_name.to_s, path].compact.join("::")
-
181
path = Inflector.underscore(full)
-
end
-
-
225
if @_eager_autoload
-
73
@_autoloads[const_name] = path
-
end
-
-
225
super const_name, path
-
end
-
-
1
def autoload_under(path)
-
5
@_under_path, old_path = path, @_under_path
-
5
yield
-
ensure
-
5
@_under_path = old_path
-
end
-
-
1
def autoload_at(path)
-
4
@_at_path, old_path = path, @_at_path
-
4
yield
-
ensure
-
4
@_at_path = old_path
-
end
-
-
1
def eager_autoload
-
8
old_eager, @_eager_autoload = @_eager_autoload, true
-
8
yield
-
ensure
-
8
@_eager_autoload = old_eager
-
end
-
-
1
def eager_load!
-
@_autoloads.values.each { |file| require file }
-
end
-
-
1
def autoloads
-
@_autoloads
-
end
-
end
-
end
-
1
require 'singleton'
-
-
1
module ActiveSupport
-
# \Deprecation specifies the API used by Rails to deprecate methods, instance
-
# variables, objects and constants.
-
1
class Deprecation
-
# active_support.rb sets an autoload for ActiveSupport::Deprecation.
-
#
-
# If these requires were at the top of the file the constant would not be
-
# defined by the time their files were loaded. Since some of them reopen
-
# ActiveSupport::Deprecation its autoload would be triggered, resulting in
-
# a circular require warning for active_support/deprecation.rb.
-
#
-
# So, we define the constant first, and load dependencies later.
-
1
require 'active_support/deprecation/instance_delegator'
-
1
require 'active_support/deprecation/behaviors'
-
1
require 'active_support/deprecation/reporting'
-
1
require 'active_support/deprecation/method_wrappers'
-
1
require 'active_support/deprecation/proxy_wrappers'
-
1
require 'active_support/core_ext/module/deprecation'
-
-
1
include Singleton
-
1
include InstanceDelegator
-
1
include Behavior
-
1
include Reporting
-
1
include MethodWrapper
-
-
# The version the deprecated behavior will be removed, by default.
-
1
attr_accessor :deprecation_horizon
-
-
# It accepts two parameters on initialization. The first is an version of library
-
# and the second is an library name
-
#
-
# ActiveSupport::Deprecation.new('2.0', 'MyLibrary')
-
1
def initialize(deprecation_horizon = '4.1', gem_name = 'Rails')
-
1
self.gem_name = gem_name
-
1
self.deprecation_horizon = deprecation_horizon
-
# By default, warnings are not silenced and debugging is off.
-
1
self.silenced = false
-
1
self.debug = false
-
end
-
end
-
end
-
1
require "active_support/notifications"
-
-
1
module ActiveSupport
-
1
class Deprecation
-
# Default warning behaviors per Rails.env.
-
1
DEFAULT_BEHAVIORS = {
-
:stderr => Proc.new { |message, callstack|
-
$stderr.puts(message)
-
$stderr.puts callstack.join("\n ") if debug
-
},
-
:log => Proc.new { |message, callstack|
-
logger =
-
if defined?(Rails) && Rails.logger
-
Rails.logger
-
else
-
require 'active_support/logger'
-
ActiveSupport::Logger.new($stderr)
-
end
-
logger.warn message
-
logger.debug callstack.join("\n ") if debug
-
},
-
:notify => Proc.new { |message, callstack|
-
ActiveSupport::Notifications.instrument("deprecation.rails",
-
:message => message, :callstack => callstack)
-
},
-
:silence => Proc.new { |message, callstack| }
-
}
-
-
1
module Behavior
-
# Whether to print a backtrace along with the warning.
-
1
attr_accessor :debug
-
-
# Returns the current behavior or if one isn't set, defaults to +:stderr+.
-
1
def behavior
-
@behavior ||= [DEFAULT_BEHAVIORS[:stderr]]
-
end
-
-
# Sets the behavior to the specified value. Can be a single value, array,
-
# or an object that responds to +call+.
-
#
-
# Available behaviors:
-
#
-
# [+stderr+] Log all deprecation warnings to +$stderr+.
-
# [+log+] Log all deprecation warnings to +Rails.logger+.
-
# [+notify+] Use +ActiveSupport::Notifications+ to notify +deprecation.rails+.
-
# [+silence+] Do nothing.
-
#
-
# Setting behaviors only affects deprecations that happen after boot time.
-
# Deprecation warnings raised by gems are not affected by this setting
-
# because they happen before Rails boots up.
-
#
-
# ActiveSupport::Deprecation.behavior = :stderr
-
# ActiveSupport::Deprecation.behavior = [:stderr, :log]
-
# ActiveSupport::Deprecation.behavior = MyCustomHandler
-
# ActiveSupport::Deprecation.behavior = proc { |message, callstack|
-
# # custom stuff
-
# }
-
1
def behavior=(behavior)
-
@behavior = Array(behavior).map { |b| DEFAULT_BEHAVIORS[b] || b }
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/kernel/singleton_class'
-
1
require 'active_support/core_ext/module/delegation'
-
-
1
module ActiveSupport
-
1
class Deprecation
-
1
module InstanceDelegator
-
1
def self.included(base)
-
1
base.extend(ClassMethods)
-
1
base.public_class_method :new
-
end
-
-
1
module ClassMethods
-
1
def include(included_module)
-
15
included_module.instance_methods.each { |m| method_added(m) }
-
3
super
-
end
-
-
1
def method_added(method_name)
-
15
singleton_class.delegate(method_name, to: :instance)
-
end
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/module/aliasing'
-
1
require 'active_support/core_ext/array/extract_options'
-
-
1
module ActiveSupport
-
1
class Deprecation
-
1
module MethodWrapper
-
# Declare that a method has been deprecated.
-
#
-
# module Fred
-
# extend self
-
#
-
# def foo; end
-
# def bar; end
-
# def baz; end
-
# end
-
#
-
# ActiveSupport::Deprecation.deprecate_methods(Fred, :foo, bar: :qux, baz: 'use Bar#baz instead')
-
# # => [:foo, :bar, :baz]
-
#
-
# Fred.foo
-
# # => "DEPRECATION WARNING: foo is deprecated and will be removed from Rails 4.1."
-
#
-
# Fred.bar
-
# # => "DEPRECATION WARNING: bar is deprecated and will be removed from Rails 4.1 (use qux instead)."
-
#
-
# Fred.baz
-
# # => "DEPRECATION WARNING: baz is deprecated and will be removed from Rails 4.1 (use Bar#baz instead)."
-
1
def deprecate_methods(target_module, *method_names)
-
options = method_names.extract_options!
-
deprecator = options.delete(:deprecator) || ActiveSupport::Deprecation.instance
-
method_names += options.keys
-
-
method_names.each do |method_name|
-
target_module.alias_method_chain(method_name, :deprecation) do |target, punctuation|
-
target_module.send(:define_method, "#{target}_with_deprecation#{punctuation}") do |*args, &block|
-
deprecator.deprecation_warning(method_name, options[method_name])
-
send(:"#{target}_without_deprecation#{punctuation}", *args, &block)
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
1
require 'active_support/inflector/methods'
-
-
1
module ActiveSupport
-
1
class Deprecation
-
1
class DeprecationProxy #:nodoc:
-
1
def self.new(*args, &block)
-
1
object = args.first
-
-
1
return object unless object
-
1
super
-
end
-
-
107
instance_methods.each { |m| undef_method m unless m =~ /^__|^object_id$/ }
-
-
# Don't give a deprecation warning on inspect since test/unit and error
-
# logs rely on it for diagnostics.
-
1
def inspect
-
target.inspect
-
end
-
-
1
private
-
1
def method_missing(called, *args, &block)
-
warn caller, called, args
-
target.__send__(called, *args, &block)
-
end
-
end
-
-
# This DeprecatedObjectProxy transforms object to depracated object.
-
#
-
# @old_object = DeprecatedObjectProxy.new(Object.new, "Don't use this object anymore!")
-
# @old_object = DeprecatedObjectProxy.new(Object.new, "Don't use this object anymore!", deprecator_instance)
-
#
-
# When someone execute any method expect +inspect+ on proxy object this will
-
# trigger +warn+ method on +deprecator_instance+.
-
#
-
# Default deprecator is <tt>ActiveSupport::Deprecation</tt>
-
1
class DeprecatedObjectProxy < DeprecationProxy
-
1
def initialize(object, message, deprecator = ActiveSupport::Deprecation.instance)
-
@object = object
-
@message = message
-
@deprecator = deprecator
-
end
-
-
1
private
-
1
def target
-
@object
-
end
-
-
1
def warn(callstack, called, args)
-
@deprecator.warn(@message, callstack)
-
end
-
end
-
-
# This DeprecatedInstanceVariableProxy transforms instance variable to
-
# depracated instance variable.
-
#
-
# class Example
-
# def initialize(deprecator)
-
# @request = ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy.new(self, :request, :@request, deprecator)
-
# @_request = :a_request
-
# end
-
#
-
# def request
-
# @_request
-
# end
-
#
-
# def old_request
-
# @request
-
# end
-
# end
-
#
-
# When someone execute any method on @request variable this will trigger
-
# +warn+ method on +deprecator_instance+ and will fetch <tt>@_request</tt>
-
# variable via +request+ method and execute the same method on non-proxy
-
# instance variable.
-
#
-
# Default deprecator is <tt>ActiveSupport::Deprecation</tt>.
-
1
class DeprecatedInstanceVariableProxy < DeprecationProxy
-
1
def initialize(instance, method, var = "@#{method}", deprecator = ActiveSupport::Deprecation.instance)
-
@instance = instance
-
@method = method
-
@var = var
-
@deprecator = deprecator
-
end
-
-
1
private
-
1
def target
-
@instance.__send__(@method)
-
end
-
-
1
def warn(callstack, called, args)
-
@deprecator.warn("#{@var} is deprecated! Call #{@method}.#{called} instead of #{@var}.#{called}. Args: #{args.inspect}", callstack)
-
end
-
end
-
-
# This DeprecatedConstantProxy transforms constant to depracated constant.
-
#
-
# OLD_CONST = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('OLD_CONST', 'NEW_CONST')
-
# OLD_CONST = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('OLD_CONST', 'NEW_CONST', deprecator_instance)
-
#
-
# When someone use old constant this will trigger +warn+ method on
-
# +deprecator_instance+.
-
#
-
# Default deprecator is <tt>ActiveSupport::Deprecation</tt>.
-
1
class DeprecatedConstantProxy < DeprecationProxy
-
1
def initialize(old_const, new_const, deprecator = ActiveSupport::Deprecation.instance)
-
1
@old_const = old_const
-
1
@new_const = new_const
-
1
@deprecator = deprecator
-
end
-
-
1
def class
-
target.class
-
end
-
-
1
private
-
1
def target
-
ActiveSupport::Inflector.constantize(@new_const.to_s)
-
end
-
-
1
def warn(callstack, called, args)
-
@deprecator.warn("#{@old_const} is deprecated! Use #{@new_const} instead.", callstack)
-
end
-
end
-
end
-
end
-
1
module ActiveSupport
-
1
class Deprecation
-
1
module Reporting
-
# Whether to print a message (silent mode)
-
1
attr_accessor :silenced
-
# Name of gem where method is deprecated
-
1
attr_accessor :gem_name
-
-
# Outputs a deprecation warning to the output configured by
-
# <tt>ActiveSupport::Deprecation.behavior</tt>.
-
#
-
# ActiveSupport::Deprecation.warn('something broke!')
-
# # => "DEPRECATION WARNING: something broke! (called from your_code.rb:1)"
-
1
def warn(message = nil, callstack = nil)
-
return if silenced
-
-
callstack ||= caller(2)
-
deprecation_message(callstack, message).tap do |m|
-
behavior.each { |b| b.call(m, callstack) }
-
end
-
end
-
-
# Silence deprecation warnings within the block.
-
#
-
# ActiveSupport::Deprecation.warn('something broke!')
-
# # => "DEPRECATION WARNING: something broke! (called from your_code.rb:1)"
-
#
-
# ActiveSupport::Deprecation.silence do
-
# ActiveSupport::Deprecation.warn('something broke!')
-
# end
-
# # => nil
-
1
def silence
-
old_silenced, @silenced = @silenced, true
-
yield
-
ensure
-
@silenced = old_silenced
-
end
-
-
1
def deprecation_warning(deprecated_method_name, message = nil, caller_backtrace = nil)
-
caller_backtrace ||= caller(2)
-
deprecated_method_warning(deprecated_method_name, message).tap do |msg|
-
warn(msg, caller_backtrace)
-
end
-
end
-
-
1
private
-
# Outputs a deprecation warning message
-
#
-
# ActiveSupport::Deprecation.deprecated_method_warning(:method_name)
-
# # => "method_name is deprecated and will be removed from Rails #{deprecation_horizon}"
-
# ActiveSupport::Deprecation.deprecated_method_warning(:method_name, :another_method)
-
# # => "method_name is deprecated and will be removed from Rails #{deprecation_horizon} (use another_method instead)"
-
# ActiveSupport::Deprecation.deprecated_method_warning(:method_name, "Optional message")
-
# # => "method_name is deprecated and will be removed from Rails #{deprecation_horizon} (Optional message)"
-
1
def deprecated_method_warning(method_name, message = nil)
-
warning = "#{method_name} is deprecated and will be removed from #{gem_name} #{deprecation_horizon}"
-
case message
-
when Symbol then "#{warning} (use #{message} instead)"
-
when String then "#{warning} (#{message})"
-
else warning
-
end
-
end
-
-
1
def deprecation_message(callstack, message = nil)
-
message ||= "You are using deprecated behavior which will be removed from the next major or minor release."
-
message += '.' unless message =~ /\.$/
-
"DEPRECATION WARNING: #{message} #{deprecation_caller_message(callstack)}"
-
end
-
-
1
def deprecation_caller_message(callstack)
-
file, line, method = extract_callstack(callstack)
-
if file
-
if line && method
-
"(called from #{method} at #{file}:#{line})"
-
else
-
"(called from #{file}:#{line})"
-
end
-
end
-
end
-
-
1
def extract_callstack(callstack)
-
rails_gem_root = File.expand_path("../../../../..", __FILE__) + "/"
-
offending_line = callstack.find { |line| !line.start_with?(rails_gem_root) } || callstack.first
-
if offending_line
-
if md = offending_line.match(/^(.+?):(\d+)(?::in `(.*?)')?/)
-
md.captures
-
else
-
offending_line
-
end
-
end
-
end
-
end
-
end
-
end
-
1
module ActiveSupport
-
# This module provides an internal implementation to track descendants
-
# which is faster than iterating through ObjectSpace.
-
1
module DescendantsTracker
-
1
@@direct_descendants = {}
-
-
1
class << self
-
1
def direct_descendants(klass)
-
@@direct_descendants[klass] || []
-
end
-
-
1
def descendants(klass)
-
10
arr = []
-
10
accumulate_descendants(klass, arr)
-
10
arr
-
end
-
-
1
def clear
-
if defined? ActiveSupport::Dependencies
-
@@direct_descendants.each do |klass, descendants|
-
if ActiveSupport::Dependencies.autoloaded?(klass)
-
@@direct_descendants.delete(klass)
-
else
-
descendants.reject! { |v| ActiveSupport::Dependencies.autoloaded?(v) }
-
end
-
end
-
else
-
@@direct_descendants.clear
-
end
-
end
-
-
# This is the only method that is not thread safe, but is only ever called
-
# during the eager loading phase.
-
1
def store_inherited(klass, descendant)
-
72
(@@direct_descendants[klass] ||= []) << descendant
-
end
-
-
1
private
-
1
def accumulate_descendants(klass, acc)
-
10
if direct_descendants = @@direct_descendants[klass]
-
acc.concat(direct_descendants)
-
direct_descendants.each { |direct_descendant| accumulate_descendants(direct_descendant, acc) }
-
end
-
end
-
end
-
-
1
def inherited(base)
-
72
DescendantsTracker.store_inherited(self, base)
-
72
super
-
end
-
-
1
def direct_descendants
-
DescendantsTracker.direct_descendants(self)
-
end
-
-
1
def descendants
-
DescendantsTracker.descendants(self)
-
end
-
end
-
end
-
1
require 'active_support/basic_object'
-
1
require 'active_support/core_ext/array/conversions'
-
1
require 'active_support/core_ext/object/acts_like'
-
-
1
module ActiveSupport
-
# Provides accurate date and time measurements using Date#advance and
-
# Time#advance, respectively. It mainly supports the methods on Numeric.
-
#
-
# 1.month.ago # equivalent to Time.now.advance(months: -1)
-
1
class Duration < BasicObject
-
1
attr_accessor :value, :parts
-
-
1
def initialize(value, parts) #:nodoc:
-
2
@value, @parts = value, parts
-
end
-
-
# Adds another Duration or a Numeric to this Duration. Numeric values
-
# are treated as seconds.
-
1
def +(other)
-
if Duration === other
-
Duration.new(value + other.value, @parts + other.parts)
-
else
-
Duration.new(value + other, @parts + [[:seconds, other]])
-
end
-
end
-
-
# Subtracts another Duration or a Numeric from this Duration. Numeric
-
# values are treated as seconds.
-
1
def -(other)
-
self + (-other)
-
end
-
-
1
def -@ #:nodoc:
-
Duration.new(-value, parts.map { |type,number| [type, -number] })
-
end
-
-
1
def is_a?(klass) #:nodoc:
-
Duration == klass || value.is_a?(klass)
-
end
-
1
alias :kind_of? :is_a?
-
-
# Returns +true+ if +other+ is also a Duration instance with the
-
# same +value+, or if <tt>other == value</tt>.
-
1
def ==(other)
-
if Duration === other
-
other.value == value
-
else
-
other == value
-
end
-
end
-
-
1
def self.===(other) #:nodoc:
-
151
other.is_a?(Duration)
-
rescue ::NoMethodError
-
false
-
end
-
-
# Calculates a new Time or Date that is as far in the future
-
# as this Duration represents.
-
1
def since(time = ::Time.current)
-
sum(1, time)
-
end
-
1
alias :from_now :since
-
-
# Calculates a new Time or Date that is as far in the past
-
# as this Duration represents.
-
1
def ago(time = ::Time.current)
-
1
sum(-1, time)
-
end
-
1
alias :until :ago
-
-
1
def inspect #:nodoc:
-
consolidated = parts.inject(::Hash.new(0)) { |h,(l,r)| h[l] += r; h }
-
parts = [:years, :months, :days, :minutes, :seconds].map do |length|
-
n = consolidated[length]
-
"#{n} #{n == 1 ? length.to_s.singularize : length.to_s}" if n.nonzero?
-
end.compact
-
parts = ["0 seconds"] if parts.empty?
-
parts.to_sentence(:locale => :en)
-
end
-
-
1
def as_json(options = nil) #:nodoc:
-
to_i
-
end
-
-
1
protected
-
-
1
def sum(sign, time = ::Time.current) #:nodoc:
-
1
parts.inject(time) do |t,(type,number)|
-
1
if t.acts_like?(:time) || t.acts_like?(:date)
-
1
if type == :seconds
-
t.since(sign * number)
-
else
-
1
t.advance(type => sign * number)
-
end
-
else
-
raise ::ArgumentError, "expected a time or date, got #{time.inspect}"
-
end
-
end
-
end
-
-
1
private
-
-
1
def method_missing(method, *args, &block) #:nodoc:
-
1
value.send(method, *args, &block)
-
end
-
end
-
end
-
1
require 'active_support/core_ext/hash/keys'
-
-
1
module ActiveSupport
-
# Implements a hash where keys <tt>:foo</tt> and <tt>"foo"</tt> are considered
-
# to be the same.
-
#
-
# rgb = ActiveSupport::HashWithIndifferentAccess.new
-
#
-
# rgb[:black] = '#000000'
-
# rgb[:black] # => '#000000'
-
# rgb['black'] # => '#000000'
-
#
-
# rgb['white'] = '#FFFFFF'
-
# rgb[:white] # => '#FFFFFF'
-
# rgb['white'] # => '#FFFFFF'
-
#
-
# Internally symbols are mapped to strings when used as keys in the entire
-
# writing interface (calling <tt>[]=</tt>, <tt>merge</tt>, etc). This
-
# mapping belongs to the public interface. For example, given:
-
#
-
# hash = ActiveSupport::HashWithIndifferentAccess.new(a: 1)
-
#
-
# You are guaranteed that the key is returned as a string:
-
#
-
# hash.keys # => ["a"]
-
#
-
# Technically other types of keys are accepted:
-
#
-
# hash = ActiveSupport::HashWithIndifferentAccess.new(a: 1)
-
# hash[0] = 0
-
# hash # => {"a"=>1, 0=>0}
-
#
-
# but this class is intended for use cases where strings or symbols are the
-
# expected keys and it is convenient to understand both as the same. For
-
# example the +params+ hash in Ruby on Rails.
-
#
-
# Note that core extensions define <tt>Hash#with_indifferent_access</tt>:
-
#
-
# rgb = { black: '#000000', white: '#FFFFFF' }.with_indifferent_access
-
#
-
# which may be handy.
-
1
class HashWithIndifferentAccess < Hash
-
# Returns +true+ so that <tt>Array#extract_options!</tt> finds members of
-
# this class.
-
1
def extractable_options?
-
true
-
end
-
-
1
def with_indifferent_access
-
1
dup
-
end
-
-
1
def nested_under_indifferent_access
-
self
-
end
-
-
1
def initialize(constructor = {})
-
5
if constructor.is_a?(Hash)
-
5
super()
-
5
update(constructor)
-
else
-
super(constructor)
-
end
-
end
-
-
1
def default(key = nil)
-
4
if key.is_a?(Symbol) && include?(key = key.to_s)
-
self[key]
-
else
-
4
super
-
end
-
end
-
-
1
def self.new_from_hash_copying_default(hash)
-
2
new(hash).tap do |new_hash|
-
2
new_hash.default = hash.default
-
end
-
end
-
-
1
def self.[](*args)
-
new.merge(Hash[*args])
-
end
-
-
1
alias_method :regular_writer, :[]= unless method_defined?(:regular_writer)
-
1
alias_method :regular_update, :update unless method_defined?(:regular_update)
-
-
# Assigns a new value to the hash:
-
#
-
# hash = ActiveSupport::HashWithIndifferentAccess.new
-
# hash[:key] = 'value'
-
#
-
# This value can be later fetched using either +:key+ or +'key'+.
-
1
def []=(key, value)
-
regular_writer(convert_key(key), convert_value(value))
-
end
-
-
1
alias_method :store, :[]=
-
-
# Updates the receiver in-place, merging in the hash passed as argument:
-
#
-
# hash_1 = ActiveSupport::HashWithIndifferentAccess.new
-
# hash_1[:key] = 'value'
-
#
-
# hash_2 = ActiveSupport::HashWithIndifferentAccess.new
-
# hash_2[:key] = 'New Value!'
-
#
-
# hash_1.update(hash_2) # => {"key"=>"New Value!"}
-
#
-
# The argument can be either an
-
# <tt>ActiveSupport::HashWithIndifferentAccess</tt> or a regular +Hash+.
-
# In either case the merge respects the semantics of indifferent access.
-
#
-
# If the argument is a regular hash with keys +:key+ and +"key"+ only one
-
# of the values end up in the receiver, but which one is unspecified.
-
#
-
# When given a block, the value for duplicated keys will be determined
-
# by the result of invoking the block with the duplicated key, the value
-
# in the receiver, and the value in +other_hash+. The rules for duplicated
-
# keys follow the semantics of indifferent access:
-
#
-
# hash_1[:key] = 10
-
# hash_2['key'] = 12
-
# hash_1.update(hash_2) { |key, old, new| old + new } # => {"key"=>22}
-
1
def update(other_hash)
-
7
if other_hash.is_a? HashWithIndifferentAccess
-
4
super(other_hash)
-
else
-
3
other_hash.each_pair do |key, value|
-
2
if block_given? && key?(key)
-
value = yield(convert_key(key), self[key], value)
-
end
-
2
regular_writer(convert_key(key), convert_value(value))
-
end
-
3
self
-
end
-
end
-
-
1
alias_method :merge!, :update
-
-
# Checks the hash for a key matching the argument passed in:
-
#
-
# hash = ActiveSupport::HashWithIndifferentAccess.new
-
# hash['key'] = 'value'
-
# hash.key?(:key) # => true
-
# hash.key?('key') # => true
-
1
def key?(key)
-
1
super(convert_key(key))
-
end
-
-
1
alias_method :include?, :key?
-
1
alias_method :has_key?, :key?
-
1
alias_method :member?, :key?
-
-
# Same as <tt>Hash#fetch</tt> where the key passed as argument can be
-
# either a string or a symbol:
-
#
-
# counters = ActiveSupport::HashWithIndifferentAccess.new
-
# counters[:foo] = 1
-
#
-
# counters.fetch('foo') # => 1
-
# counters.fetch(:bar, 0) # => 0
-
# counters.fetch(:bar) {|key| 0} # => 0
-
# counters.fetch(:zoo) # => KeyError: key not found: "zoo"
-
1
def fetch(key, *extras)
-
super(convert_key(key), *extras)
-
end
-
-
# Returns an array of the values at the specified indices:
-
#
-
# hash = ActiveSupport::HashWithIndifferentAccess.new
-
# hash[:a] = 'x'
-
# hash[:b] = 'y'
-
# hash.values_at('a', 'b') # => ["x", "y"]
-
1
def values_at(*indices)
-
indices.collect {|key| self[convert_key(key)]}
-
end
-
-
# Returns an exact copy of the hash.
-
1
def dup
-
3
self.class.new(self).tap do |new_hash|
-
3
new_hash.default = default
-
end
-
end
-
-
# This method has the same semantics of +update+, except it does not
-
# modify the receiver but rather returns a new hash with indifferent
-
# access with the result of the merge.
-
1
def merge(hash, &block)
-
1
self.dup.update(hash, &block)
-
end
-
-
# Like +merge+ but the other way around: Merges the receiver into the
-
# argument and returns a new hash with indifferent access as result:
-
#
-
# hash = ActiveSupport::HashWithIndifferentAccess.new
-
# hash['a'] = nil
-
# hash.reverse_merge(a: 0, b: 1) # => {"a"=>nil, "b"=>1}
-
1
def reverse_merge(other_hash)
-
super(self.class.new_from_hash_copying_default(other_hash))
-
end
-
-
# Same semantics as +reverse_merge+ but modifies the receiver in-place.
-
1
def reverse_merge!(other_hash)
-
replace(reverse_merge( other_hash ))
-
end
-
-
# Replaces the contents of this hash with other_hash.
-
#
-
# h = { "a" => 100, "b" => 200 }
-
# h.replace({ "c" => 300, "d" => 400 }) #=> {"c"=>300, "d"=>400}
-
1
def replace(other_hash)
-
super(self.class.new_from_hash_copying_default(other_hash))
-
end
-
-
# Removes the specified key from the hash.
-
1
def delete(key)
-
super(convert_key(key))
-
end
-
-
1
def stringify_keys!; self end
-
1
def deep_stringify_keys!; self end
-
1
def stringify_keys; dup end
-
1
def deep_stringify_keys; dup end
-
1
undef :symbolize_keys!
-
1
undef :deep_symbolize_keys!
-
1
def symbolize_keys; to_hash.symbolize_keys end
-
1
def deep_symbolize_keys; to_hash.deep_symbolize_keys end
-
1
def to_options!; self end
-
-
# Convert to a regular hash with string keys.
-
1
def to_hash
-
Hash.new(default).merge!(self)
-
end
-
-
1
protected
-
1
def convert_key(key)
-
3
key.kind_of?(Symbol) ? key.to_s : key
-
end
-
-
1
def convert_value(value)
-
2
if value.is_a? Hash
-
value.nested_under_indifferent_access
-
2
elsif value.is_a?(Array)
-
value = value.dup if value.frozen?
-
value.map! { |e| convert_value(e) }
-
else
-
2
value
-
end
-
end
-
end
-
end
-
-
1
HashWithIndifferentAccess = ActiveSupport::HashWithIndifferentAccess
-
1
begin
-
1
require 'i18n'
-
1
require 'active_support/lazy_load_hooks'
-
rescue LoadError => e
-
$stderr.puts "The i18n gem is not available. Please add it to your Gemfile and run bundle install"
-
raise e
-
end
-
-
1
ActiveSupport.run_load_hooks(:i18n)
-
1
I18n.load_path << "#{File.dirname(__FILE__)}/locale/en.yml"
-
1
require 'active_support/inflector/inflections'
-
-
1
module ActiveSupport
-
1
Inflector.inflections(:en) do |inflect|
-
1
inflect.plural(/$/, 's')
-
1
inflect.plural(/s$/i, 's')
-
1
inflect.plural(/^(ax|test)is$/i, '\1es')
-
1
inflect.plural(/(octop|vir)us$/i, '\1i')
-
1
inflect.plural(/(octop|vir)i$/i, '\1i')
-
1
inflect.plural(/(alias|status)$/i, '\1es')
-
1
inflect.plural(/(bu)s$/i, '\1ses')
-
1
inflect.plural(/(buffal|tomat)o$/i, '\1oes')
-
1
inflect.plural(/([ti])um$/i, '\1a')
-
1
inflect.plural(/([ti])a$/i, '\1a')
-
1
inflect.plural(/sis$/i, 'ses')
-
1
inflect.plural(/(?:([^f])fe|([lr])f)$/i, '\1\2ves')
-
1
inflect.plural(/(hive)$/i, '\1s')
-
1
inflect.plural(/([^aeiouy]|qu)y$/i, '\1ies')
-
1
inflect.plural(/(x|ch|ss|sh)$/i, '\1es')
-
1
inflect.plural(/(matr|vert|ind)(?:ix|ex)$/i, '\1ices')
-
1
inflect.plural(/^(m|l)ouse$/i, '\1ice')
-
1
inflect.plural(/^(m|l)ice$/i, '\1ice')
-
1
inflect.plural(/^(ox)$/i, '\1en')
-
1
inflect.plural(/^(oxen)$/i, '\1')
-
1
inflect.plural(/(quiz)$/i, '\1zes')
-
-
1
inflect.singular(/s$/i, '')
-
1
inflect.singular(/(ss)$/i, '\1')
-
1
inflect.singular(/(n)ews$/i, '\1ews')
-
1
inflect.singular(/([ti])a$/i, '\1um')
-
1
inflect.singular(/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)(sis|ses)$/i, '\1sis')
-
1
inflect.singular(/(^analy)(sis|ses)$/i, '\1sis')
-
1
inflect.singular(/([^f])ves$/i, '\1fe')
-
1
inflect.singular(/(hive)s$/i, '\1')
-
1
inflect.singular(/(tive)s$/i, '\1')
-
1
inflect.singular(/([lr])ves$/i, '\1f')
-
1
inflect.singular(/([^aeiouy]|qu)ies$/i, '\1y')
-
1
inflect.singular(/(s)eries$/i, '\1eries')
-
1
inflect.singular(/(m)ovies$/i, '\1ovie')
-
1
inflect.singular(/(x|ch|ss|sh)es$/i, '\1')
-
1
inflect.singular(/^(m|l)ice$/i, '\1ouse')
-
1
inflect.singular(/(bus)(es)?$/i, '\1')
-
1
inflect.singular(/(o)es$/i, '\1')
-
1
inflect.singular(/(shoe)s$/i, '\1')
-
1
inflect.singular(/(cris|test)(is|es)$/i, '\1is')
-
1
inflect.singular(/^(a)x[ie]s$/i, '\1xis')
-
1
inflect.singular(/(octop|vir)(us|i)$/i, '\1us')
-
1
inflect.singular(/(alias|status)(es)?$/i, '\1')
-
1
inflect.singular(/^(ox)en/i, '\1')
-
1
inflect.singular(/(vert|ind)ices$/i, '\1ex')
-
1
inflect.singular(/(matr)ices$/i, '\1ix')
-
1
inflect.singular(/(quiz)zes$/i, '\1')
-
1
inflect.singular(/(database)s$/i, '\1')
-
-
1
inflect.irregular('person', 'people')
-
1
inflect.irregular('man', 'men')
-
1
inflect.irregular('child', 'children')
-
1
inflect.irregular('sex', 'sexes')
-
1
inflect.irregular('move', 'moves')
-
1
inflect.irregular('cow', 'kine')
-
1
inflect.irregular('zombie', 'zombies')
-
-
1
inflect.uncountable(%w(equipment information rice money species series fish sheep jeans police))
-
end
-
end
-
# in case active_support/inflector is required without the rest of active_support
-
1
require 'active_support/inflector/inflections'
-
1
require 'active_support/inflector/transliterate'
-
1
require 'active_support/inflector/methods'
-
-
1
require 'active_support/inflections'
-
1
require 'active_support/core_ext/string/inflections'
-
1
require 'active_support/core_ext/array/prepend_and_append'
-
1
require 'active_support/i18n'
-
-
1
module ActiveSupport
-
1
module Inflector
-
1
extend self
-
-
# A singleton instance of this class is yielded by Inflector.inflections,
-
# which can then be used to specify additional inflection rules. If passed
-
# an optional locale, rules for other languages can be specified. The
-
# default locale is <tt>:en</tt>. Only rules for English are provided.
-
#
-
# ActiveSupport::Inflector.inflections(:en) do |inflect|
-
# inflect.plural /^(ox)$/i, '\1\2en'
-
# inflect.singular /^(ox)en/i, '\1'
-
#
-
# inflect.irregular 'octopus', 'octopi'
-
#
-
# inflect.uncountable 'equipment'
-
# end
-
#
-
# New rules are added at the top. So in the example above, the irregular
-
# rule for octopus will now be the first of the pluralization and
-
# singularization rules that is runs. This guarantees that your rules run
-
# before any of the rules that may already have been loaded.
-
1
class Inflections
-
1
def self.instance(locale = :en)
-
688
@__instance__ ||= Hash.new { |h, k| h[k] = new }
-
687
@__instance__[locale]
-
end
-
-
1
attr_reader :plurals, :singulars, :uncountables, :humans, :acronyms, :acronym_regex
-
-
1
def initialize
-
1
@plurals, @singulars, @uncountables, @humans, @acronyms, @acronym_regex = [], [], [], [], {}, /(?=a)b/
-
end
-
-
# Private, for the test suite.
-
1
def initialize_dup(orig) # :nodoc:
-
%w(plurals singulars uncountables humans acronyms acronym_regex).each do |scope|
-
instance_variable_set("@#{scope}", orig.send(scope).dup)
-
end
-
end
-
-
# Specifies a new acronym. An acronym must be specified as it will appear
-
# in a camelized string. An underscore string that contains the acronym
-
# will retain the acronym when passed to +camelize+, +humanize+, or
-
# +titleize+. A camelized string that contains the acronym will maintain
-
# the acronym when titleized or humanized, and will convert the acronym
-
# into a non-delimited single lowercase word when passed to +underscore+.
-
#
-
# acronym 'HTML'
-
# titleize 'html' #=> 'HTML'
-
# camelize 'html' #=> 'HTML'
-
# underscore 'MyHTML' #=> 'my_html'
-
#
-
# The acronym, however, must occur as a delimited unit and not be part of
-
# another word for conversions to recognize it:
-
#
-
# acronym 'HTTP'
-
# camelize 'my_http_delimited' #=> 'MyHTTPDelimited'
-
# camelize 'https' #=> 'Https', not 'HTTPs'
-
# underscore 'HTTPS' #=> 'http_s', not 'https'
-
#
-
# acronym 'HTTPS'
-
# camelize 'https' #=> 'HTTPS'
-
# underscore 'HTTPS' #=> 'https'
-
#
-
# Note: Acronyms that are passed to +pluralize+ will no longer be
-
# recognized, since the acronym will not occur as a delimited unit in the
-
# pluralized result. To work around this, you must specify the pluralized
-
# form as an acronym as well:
-
#
-
# acronym 'API'
-
# camelize(pluralize('api')) #=> 'Apis'
-
#
-
# acronym 'APIs'
-
# camelize(pluralize('api')) #=> 'APIs'
-
#
-
# +acronym+ may be used to specify any word that contains an acronym or
-
# otherwise needs to maintain a non-standard capitalization. The only
-
# restriction is that the word must begin with a capital letter.
-
#
-
# acronym 'RESTful'
-
# underscore 'RESTful' #=> 'restful'
-
# underscore 'RESTfulController' #=> 'restful_controller'
-
# titleize 'RESTfulController' #=> 'RESTful Controller'
-
# camelize 'restful' #=> 'RESTful'
-
# camelize 'restful_controller' #=> 'RESTfulController'
-
#
-
# acronym 'McDonald'
-
# underscore 'McDonald' #=> 'mcdonald'
-
# camelize 'mcdonald' #=> 'McDonald'
-
1
def acronym(word)
-
@acronyms[word.downcase] = word
-
@acronym_regex = /#{@acronyms.values.join("|")}/
-
end
-
-
# Specifies a new pluralization rule and its replacement. The rule can
-
# either be a string or a regular expression. The replacement should
-
# always be a string that may include references to the matched data from
-
# the rule.
-
1
def plural(rule, replacement)
-
37
@uncountables.delete(rule) if rule.is_a?(String)
-
37
@uncountables.delete(replacement)
-
37
@plurals.prepend([rule, replacement])
-
end
-
-
# Specifies a new singularization rule and its replacement. The rule can
-
# either be a string or a regular expression. The replacement should
-
# always be a string that may include references to the matched data from
-
# the rule.
-
1
def singular(rule, replacement)
-
35
@uncountables.delete(rule) if rule.is_a?(String)
-
35
@uncountables.delete(replacement)
-
35
@singulars.prepend([rule, replacement])
-
end
-
-
# Specifies a new irregular that applies to both pluralization and
-
# singularization at the same time. This can only be used for strings, not
-
# regular expressions. You simply pass the irregular in singular and
-
# plural form.
-
#
-
# irregular 'octopus', 'octopi'
-
# irregular 'person', 'people'
-
1
def irregular(singular, plural)
-
7
@uncountables.delete(singular)
-
7
@uncountables.delete(plural)
-
7
if singular[0,1].upcase == plural[0,1].upcase
-
6
plural(Regexp.new("(#{singular[0,1]})#{singular[1..-1]}$", "i"), '\1' + plural[1..-1])
-
6
plural(Regexp.new("(#{plural[0,1]})#{plural[1..-1]}$", "i"), '\1' + plural[1..-1])
-
6
singular(Regexp.new("(#{plural[0,1]})#{plural[1..-1]}$", "i"), '\1' + singular[1..-1])
-
else
-
1
plural(Regexp.new("#{singular[0,1].upcase}(?i)#{singular[1..-1]}$"), plural[0,1].upcase + plural[1..-1])
-
1
plural(Regexp.new("#{singular[0,1].downcase}(?i)#{singular[1..-1]}$"), plural[0,1].downcase + plural[1..-1])
-
1
plural(Regexp.new("#{plural[0,1].upcase}(?i)#{plural[1..-1]}$"), plural[0,1].upcase + plural[1..-1])
-
1
plural(Regexp.new("#{plural[0,1].downcase}(?i)#{plural[1..-1]}$"), plural[0,1].downcase + plural[1..-1])
-
1
singular(Regexp.new("#{plural[0,1].upcase}(?i)#{plural[1..-1]}$"), singular[0,1].upcase + singular[1..-1])
-
1
singular(Regexp.new("#{plural[0,1].downcase}(?i)#{plural[1..-1]}$"), singular[0,1].downcase + singular[1..-1])
-
end
-
end
-
-
# Add uncountable words that shouldn't be attempted inflected.
-
#
-
# uncountable 'money'
-
# uncountable 'money', 'information'
-
# uncountable %w( money information rice )
-
1
def uncountable(*words)
-
1
(@uncountables << words).flatten!
-
end
-
-
# Specifies a humanized form of a string by a regular expression rule or
-
# by a string mapping. When using a regular expression based replacement,
-
# the normal humanize formatting is called after the replacement. When a
-
# string is used, the human form should be specified as desired (example:
-
# 'The name', not 'the_name').
-
#
-
# human /_cnt$/i, '\1_count'
-
# human 'legacy_col_person_name', 'Name'
-
1
def human(rule, replacement)
-
@humans.prepend([rule, replacement])
-
end
-
-
# Clears the loaded inflections within a given scope (default is
-
# <tt>:all</tt>). Give the scope as a symbol of the inflection type, the
-
# options are: <tt>:plurals</tt>, <tt>:singulars</tt>, <tt>:uncountables</tt>,
-
# <tt>:humans</tt>.
-
#
-
# clear :all
-
# clear :plurals
-
1
def clear(scope = :all)
-
case scope
-
when :all
-
@plurals, @singulars, @uncountables, @humans = [], [], [], []
-
else
-
instance_variable_set "@#{scope}", []
-
end
-
end
-
end
-
-
# Yields a singleton instance of Inflector::Inflections so you can specify
-
# additional inflector rules. If passed an optional locale, rules for other
-
# languages can be specified. If not specified, defaults to <tt>:en</tt>.
-
# Only rules for English are provided.
-
#
-
# ActiveSupport::Inflector.inflections(:en) do |inflect|
-
# inflect.uncountable 'rails'
-
# end
-
1
def inflections(locale = :en)
-
687
if block_given?
-
1
yield Inflections.instance(locale)
-
else
-
686
Inflections.instance(locale)
-
end
-
end
-
end
-
end
-
# encoding: utf-8
-
-
1
require 'active_support/inflector/inflections'
-
1
require 'active_support/inflections'
-
-
1
module ActiveSupport
-
# The Inflector transforms words from singular to plural, class names to table
-
# names, modularized class names to ones without, and class names to foreign
-
# keys. The default inflections for pluralization, singularization, and
-
# uncountable words are kept in inflections.rb.
-
#
-
# The Rails core team has stated patches for the inflections library will not
-
# be accepted in order to avoid breaking legacy applications which may be
-
# relying on errant inflections. If you discover an incorrect inflection and
-
# require it for your application or wish to define rules for languages other
-
# than English, please correct or add them yourself (explained below).
-
1
module Inflector
-
1
extend self
-
-
# Returns the plural form of the word in the string.
-
#
-
# If passed an optional +locale+ parameter, the word will be
-
# pluralized using rules defined for that language. By default,
-
# this parameter is set to <tt>:en</tt>.
-
#
-
# 'post'.pluralize # => "posts"
-
# 'octopus'.pluralize # => "octopi"
-
# 'sheep'.pluralize # => "sheep"
-
# 'words'.pluralize # => "words"
-
# 'CamelOctopus'.pluralize # => "CamelOctopi"
-
# 'ley'.pluralize(:es) # => "leyes"
-
1
def pluralize(word, locale = :en)
-
apply_inflections(word, inflections(locale).plurals)
-
end
-
-
# The reverse of +pluralize+, returns the singular form of a word in a
-
# string.
-
#
-
# If passed an optional +locale+ parameter, the word will be
-
# pluralized using rules defined for that language. By default,
-
# this parameter is set to <tt>:en</tt>.
-
#
-
# 'posts'.singularize # => "post"
-
# 'octopi'.singularize # => "octopus"
-
# 'sheep'.singularize # => "sheep"
-
# 'word'.singularize # => "word"
-
# 'CamelOctopi'.singularize # => "CamelOctopus"
-
# 'leyes'.singularize(:es) # => "ley"
-
1
def singularize(word, locale = :en)
-
apply_inflections(word, inflections(locale).singulars)
-
end
-
-
# By default, +camelize+ converts strings to UpperCamelCase. If the argument
-
# to +camelize+ is set to <tt>:lower</tt> then +camelize+ produces
-
# lowerCamelCase.
-
#
-
# +camelize+ will also convert '/' to '::' which is useful for converting
-
# paths to namespaces.
-
#
-
# 'active_model'.camelize # => "ActiveModel"
-
# 'active_model'.camelize(:lower) # => "activeModel"
-
# 'active_model/errors'.camelize # => "ActiveModel::Errors"
-
# 'active_model/errors'.camelize(:lower) # => "activeModel::Errors"
-
#
-
# As a rule of thumb you can think of +camelize+ as the inverse of
-
# +underscore+, though there are cases where that does not hold:
-
#
-
# 'SSLError'.underscore.camelize # => "SslError"
-
1
def camelize(term, uppercase_first_letter = true)
-
11
string = term.to_s
-
11
if uppercase_first_letter
-
22
string = string.sub(/^[a-z\d]*/) { inflections.acronyms[$&] || $&.capitalize }
-
else
-
string = string.sub(/^(?:#{inflections.acronym_regex}(?=\b|[A-Z_])|\w)/) { $&.downcase }
-
end
-
31
string.gsub(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{inflections.acronyms[$2] || $2.capitalize}" }.gsub('/', '::')
-
end
-
-
# Makes an underscored, lowercase form from the expression in the string.
-
#
-
# Changes '::' to '/' to convert namespaces to paths.
-
#
-
# 'ActiveModel'.underscore # => "active_model"
-
# 'ActiveModel::Errors'.underscore # => "active_model/errors"
-
#
-
# As a rule of thumb you can think of +underscore+ as the inverse of
-
# +camelize+, though there are cases where that does not hold:
-
#
-
# 'SSLError'.underscore.camelize # => "SslError"
-
1
def underscore(camel_cased_word)
-
285
word = camel_cased_word.to_s.dup
-
285
word.gsub!('::', '/')
-
285
word.gsub!(/(?:([A-Za-z\d])|^)(#{inflections.acronym_regex})(?=\b|[^a-z])/) { "#{$1}#{$1 && '_'}#{$2.downcase}" }
-
285
word.gsub!(/([A-Z\d]+)([A-Z][a-z])/,'\1_\2')
-
285
word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
-
285
word.tr!("-", "_")
-
285
word.downcase!
-
285
word
-
end
-
-
# Capitalizes the first word and turns underscores into spaces and strips a
-
# trailing "_id", if any. Like +titleize+, this is meant for creating pretty
-
# output.
-
#
-
# 'employee_salary'.humanize # => "Employee salary"
-
# 'author_id'.humanize # => "Author"
-
1
def humanize(lower_case_and_underscored_word)
-
75
result = lower_case_and_underscored_word.to_s.dup
-
75
inflections.humans.each { |(rule, replacement)| break if result.sub!(rule, replacement) }
-
75
result.gsub!(/_id$/, "")
-
75
result.tr!('_', ' ')
-
75
result.gsub(/([a-z\d]*)/i) { |match|
-
295
"#{inflections.acronyms[match] || match.downcase}"
-
75
}.gsub(/^\w/) { $&.upcase }
-
end
-
-
# Capitalizes all the words and replaces some characters in the string to
-
# create a nicer looking title. +titleize+ is meant for creating pretty
-
# output. It is not used in the Rails internals.
-
#
-
# +titleize+ is also aliased as +titlecase+.
-
#
-
# 'man from the boondocks'.titleize # => "Man From The Boondocks"
-
# 'x-men: the last stand'.titleize # => "X Men: The Last Stand"
-
# 'TheManWithoutAPast'.titleize # => "The Man Without A Past"
-
# 'raiders_of_the_lost_ark'.titleize # => "Raiders Of The Lost Ark"
-
1
def titleize(word)
-
humanize(underscore(word)).gsub(/\b(?<!['’`])[a-z]/) { $&.capitalize }
-
end
-
-
# Create the name of a table like Rails does for models to table names. This
-
# method uses the +pluralize+ method on the last word in the string.
-
#
-
# 'RawScaledScorer'.tableize # => "raw_scaled_scorers"
-
# 'egg_and_ham'.tableize # => "egg_and_hams"
-
# 'fancyCategory'.tableize # => "fancy_categories"
-
1
def tableize(class_name)
-
pluralize(underscore(class_name))
-
end
-
-
# Create a class name from a plural table name like Rails does for table
-
# names to models. Note that this returns a string and not a Class (To
-
# convert to an actual class follow +classify+ with +constantize+).
-
#
-
# 'egg_and_hams'.classify # => "EggAndHam"
-
# 'posts'.classify # => "Post"
-
#
-
# Singular names are not handled correctly:
-
#
-
# 'business'.classify # => "Busines"
-
1
def classify(table_name)
-
# strip out any leading schema name
-
camelize(singularize(table_name.to_s.sub(/.*\./, '')))
-
end
-
-
# Replaces underscores with dashes in the string.
-
#
-
# 'puni_puni'.dasherize # => "puni-puni"
-
1
def dasherize(underscored_word)
-
6
underscored_word.tr('_', '-')
-
end
-
-
# Removes the module part from the expression in the string.
-
#
-
# 'ActiveRecord::CoreExtensions::String::Inflections'.demodulize # => "Inflections"
-
# 'Inflections'.demodulize # => "Inflections"
-
#
-
# See also +deconstantize+.
-
1
def demodulize(path)
-
path = path.to_s
-
if i = path.rindex('::')
-
path[(i+2)..-1]
-
else
-
path
-
end
-
end
-
-
# Removes the rightmost segment from the constant expression in the string.
-
#
-
# 'Net::HTTP'.deconstantize # => "Net"
-
# '::Net::HTTP'.deconstantize # => "::Net"
-
# 'String'.deconstantize # => ""
-
# '::String'.deconstantize # => ""
-
# ''.deconstantize # => ""
-
#
-
# See also +demodulize+.
-
1
def deconstantize(path)
-
path.to_s[0...(path.rindex('::') || 0)] # implementation based on the one in facets' Module#spacename
-
end
-
-
# Creates a foreign key name from a class name.
-
# +separate_class_name_and_id_with_underscore+ sets whether
-
# the method should put '_' between the name and 'id'.
-
#
-
# 'Message'.foreign_key # => "message_id"
-
# 'Message'.foreign_key(false) # => "messageid"
-
# 'Admin::Post'.foreign_key # => "post_id"
-
1
def foreign_key(class_name, separate_class_name_and_id_with_underscore = true)
-
underscore(demodulize(class_name)) + (separate_class_name_and_id_with_underscore ? "_id" : "id")
-
end
-
-
# Tries to find a constant with the name specified in the argument string.
-
#
-
# 'Module'.constantize # => Module
-
# 'Test::Unit'.constantize # => Test::Unit
-
#
-
# The name is assumed to be the one of a top-level constant, no matter
-
# whether it starts with "::" or not. No lexical context is taken into
-
# account:
-
#
-
# C = 'outside'
-
# module M
-
# C = 'inside'
-
# C # => 'inside'
-
# 'C'.constantize # => 'outside', same as ::C
-
# end
-
#
-
# NameError is raised when the name is not in CamelCase or the constant is
-
# unknown.
-
1
def constantize(camel_cased_word)
-
33
names = camel_cased_word.split('::')
-
33
names.shift if names.empty? || names.first.empty?
-
-
33
names.inject(Object) do |constant, name|
-
42
if constant == Object
-
33
constant.const_get(name)
-
else
-
9
candidate = constant.const_get(name)
-
4
next candidate if constant.const_defined?(name, false)
-
next candidate unless Object.const_defined?(name)
-
-
# Go down the ancestors to check it it's owned
-
# directly before we reach Object or the end of ancestors.
-
constant = constant.ancestors.inject do |const, ancestor|
-
break const if ancestor == Object
-
break ancestor if ancestor.const_defined?(name, false)
-
const
-
end
-
-
# owner is in Object, so raise
-
constant.const_get(name, false)
-
end
-
end
-
end
-
-
# Tries to find a constant with the name specified in the argument string.
-
#
-
# 'Module'.safe_constantize # => Module
-
# 'Test::Unit'.safe_constantize # => Test::Unit
-
#
-
# The name is assumed to be the one of a top-level constant, no matter
-
# whether it starts with "::" or not. No lexical context is taken into
-
# account:
-
#
-
# C = 'outside'
-
# module M
-
# C = 'inside'
-
# C # => 'inside'
-
# 'C'.safe_constantize # => 'outside', same as ::C
-
# end
-
#
-
# +nil+ is returned when the name is not in CamelCase or the constant (or
-
# part of it) is unknown.
-
#
-
# 'blargle'.safe_constantize # => nil
-
# 'UnknownModule'.safe_constantize # => nil
-
# 'UnknownModule::Foo::Bar'.safe_constantize # => nil
-
1
def safe_constantize(camel_cased_word)
-
begin
-
constantize(camel_cased_word)
-
rescue NameError => e
-
raise unless e.message =~ /(uninitialized constant|wrong constant name) #{const_regexp(camel_cased_word)}$/ ||
-
e.name.to_s == camel_cased_word.to_s
-
rescue ArgumentError => e
-
raise unless e.message =~ /not missing constant #{const_regexp(camel_cased_word)}\!$/
-
end
-
end
-
-
# Returns the suffix that should be added to a number to denote the position
-
# in an ordered sequence such as 1st, 2nd, 3rd, 4th.
-
#
-
# ordinal(1) # => "st"
-
# ordinal(2) # => "nd"
-
# ordinal(1002) # => "nd"
-
# ordinal(1003) # => "rd"
-
# ordinal(-11) # => "th"
-
# ordinal(-1021) # => "st"
-
1
def ordinal(number)
-
abs_number = number.to_i.abs
-
-
if (11..13).include?(abs_number % 100)
-
"th"
-
else
-
case abs_number % 10
-
when 1; "st"
-
when 2; "nd"
-
when 3; "rd"
-
else "th"
-
end
-
end
-
end
-
-
# Turns a number into an ordinal string used to denote the position in an
-
# ordered sequence such as 1st, 2nd, 3rd, 4th.
-
#
-
# ordinalize(1) # => "1st"
-
# ordinalize(2) # => "2nd"
-
# ordinalize(1002) # => "1002nd"
-
# ordinalize(1003) # => "1003rd"
-
# ordinalize(-11) # => "-11th"
-
# ordinalize(-1021) # => "-1021st"
-
1
def ordinalize(number)
-
"#{number}#{ordinal(number)}"
-
end
-
-
1
private
-
-
# Mount a regular expression that will match part by part of the constant.
-
# For instance, Foo::Bar::Baz will generate Foo(::Bar(::Baz)?)?
-
1
def const_regexp(camel_cased_word) #:nodoc:
-
parts = camel_cased_word.split("::")
-
last = parts.pop
-
-
parts.reverse.inject(last) do |acc, part|
-
part.empty? ? acc : "#{part}(::#{acc})?"
-
end
-
end
-
-
# Applies inflection rules for +singularize+ and +pluralize+.
-
#
-
# apply_inflections('post', inflections.plurals) # => "posts"
-
# apply_inflections('posts', inflections.singulars) # => "post"
-
1
def apply_inflections(word, rules)
-
result = word.to_s.dup
-
-
if word.empty? || inflections.uncountables.include?(result.downcase[/\b\w+\Z/])
-
result
-
else
-
rules.each { |(rule, replacement)| break if result.sub!(rule, replacement) }
-
result
-
end
-
end
-
end
-
end
-
# encoding: utf-8
-
1
require 'active_support/core_ext/string/multibyte'
-
1
require 'active_support/i18n'
-
-
1
module ActiveSupport
-
1
module Inflector
-
-
# Replaces non-ASCII characters with an ASCII approximation, or if none
-
# exists, a replacement character which defaults to "?".
-
#
-
# transliterate('Ærøskøbing')
-
# # => "AEroskobing"
-
#
-
# Default approximations are provided for Western/Latin characters,
-
# e.g, "ø", "ñ", "é", "ß", etc.
-
#
-
# This method is I18n aware, so you can set up custom approximations for a
-
# locale. This can be useful, for example, to transliterate German's "ü"
-
# and "ö" to "ue" and "oe", or to add support for transliterating Russian
-
# to ASCII.
-
#
-
# In order to make your custom transliterations available, you must set
-
# them as the <tt>i18n.transliterate.rule</tt> i18n key:
-
#
-
# # Store the transliterations in locales/de.yml
-
# i18n:
-
# transliterate:
-
# rule:
-
# ü: "ue"
-
# ö: "oe"
-
#
-
# # Or set them using Ruby
-
# I18n.backend.store_translations(:de, i18n: {
-
# transliterate: {
-
# rule: {
-
# 'ü' => 'ue',
-
# 'ö' => 'oe'
-
# }
-
# }
-
# })
-
#
-
# The value for <tt>i18n.transliterate.rule</tt> can be a simple Hash that
-
# maps characters to ASCII approximations as shown above, or, for more
-
# complex requirements, a Proc:
-
#
-
# I18n.backend.store_translations(:de, i18n: {
-
# transliterate: {
-
# rule: ->(string) { MyTransliterator.transliterate(string) }
-
# }
-
# })
-
#
-
# Now you can have different transliterations for each locale:
-
#
-
# I18n.locale = :en
-
# transliterate('Jürgen')
-
# # => "Jurgen"
-
#
-
# I18n.locale = :de
-
# transliterate('Jürgen')
-
# # => "Juergen"
-
1
def transliterate(string, replacement = "?")
-
I18n.transliterate(ActiveSupport::Multibyte::Unicode.normalize(
-
ActiveSupport::Multibyte::Unicode.tidy_bytes(string), :c),
-
:replacement => replacement)
-
end
-
-
# Replaces special characters in a string so that it may be used as part of
-
# a 'pretty' URL.
-
#
-
# class Person
-
# def to_param
-
# "#{id}-#{name.parameterize}"
-
# end
-
# end
-
#
-
# @person = Person.find(1)
-
# # => #<Person id: 1, name: "Donald E. Knuth">
-
#
-
# <%= link_to(@person.name, person_path(@person)) %>
-
# # => <a href="/person/1-donald-e-knuth">Donald E. Knuth</a>
-
1
def parameterize(string, sep = '-')
-
# replace accented chars with their ascii equivalents
-
parameterized_string = transliterate(string)
-
# Turn unwanted chars into the separator
-
parameterized_string.gsub!(/[^a-z0-9\-_]+/i, sep)
-
unless sep.nil? || sep.empty?
-
re_sep = Regexp.escape(sep)
-
# No more than one of the separator in a row.
-
parameterized_string.gsub!(/#{re_sep}{2,}/, sep)
-
# Remove leading/trailing separator.
-
parameterized_string.gsub!(/^#{re_sep}|#{re_sep}$/i, '')
-
end
-
parameterized_string.downcase
-
end
-
-
end
-
end
-
1
require 'active_support/json/decoding'
-
1
require 'active_support/json/encoding'
-
1
require 'active_support/core_ext/module/attribute_accessors'
-
1
require 'active_support/core_ext/module/delegation'
-
1
require 'multi_json'
-
-
1
module ActiveSupport
-
# Look for and parse json strings that look like ISO 8601 times.
-
1
mattr_accessor :parse_json_times
-
-
1
module JSON
-
1
class << self
-
# Parses a JSON string (JavaScript Object Notation) into a hash.
-
# See www.json.org for more info.
-
#
-
# ActiveSupport::JSON.decode("{\"team\":\"rails\",\"players\":\"36\"}")
-
# => {"team" => "rails", "players" => "36"}
-
1
def decode(json, options ={})
-
data = MultiJson.load(json, options)
-
if ActiveSupport.parse_json_times
-
convert_dates_from(data)
-
else
-
data
-
end
-
end
-
-
1
def engine
-
MultiJson.adapter
-
end
-
1
alias :backend :engine
-
-
1
def engine=(name)
-
MultiJson.use(name)
-
end
-
1
alias :backend= :engine=
-
-
1
def with_backend(name)
-
old_backend, self.backend = backend, name
-
yield
-
ensure
-
self.backend = old_backend
-
end
-
-
# Returns the class of the error that will be raised when there is an
-
# error in decoding JSON. Using this method means you won't directly
-
# depend on the ActiveSupport's JSON implementation, in case it changes
-
# in the future.
-
#
-
# begin
-
# obj = ActiveSupport::JSON.decode(some_string)
-
# rescue ActiveSupport::JSON.parse_error
-
# Rails.logger.warn("Attempted to decode invalid JSON: #{some_string}")
-
# end
-
1
def parse_error
-
MultiJson::DecodeError
-
end
-
-
1
private
-
-
1
def convert_dates_from(data)
-
case data
-
when nil
-
nil
-
when DATE_REGEX
-
begin
-
DateTime.parse(data)
-
rescue ArgumentError
-
data
-
end
-
when Array
-
data.map! { |d| convert_dates_from(d) }
-
when Hash
-
data.each do |key, value|
-
data[key] = convert_dates_from(value)
-
end
-
else
-
data
-
end
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/object/to_json'
-
1
require 'active_support/core_ext/module/delegation'
-
1
require 'active_support/json/variable'
-
-
1
require 'bigdecimal'
-
1
require 'active_support/core_ext/big_decimal/conversions' # for #to_s
-
1
require 'active_support/core_ext/hash/except'
-
1
require 'active_support/core_ext/hash/slice'
-
1
require 'active_support/core_ext/object/instance_variables'
-
1
require 'time'
-
1
require 'active_support/core_ext/time/conversions'
-
1
require 'active_support/core_ext/date_time/conversions'
-
1
require 'active_support/core_ext/date/conversions'
-
1
require 'set'
-
-
1
module ActiveSupport
-
1
class << self
-
1
delegate :use_standard_json_time_format, :use_standard_json_time_format=,
-
:escape_html_entities_in_json, :escape_html_entities_in_json=,
-
:encode_big_decimal_as_string, :encode_big_decimal_as_string=,
-
:to => :'ActiveSupport::JSON::Encoding'
-
end
-
-
1
module JSON
-
# matches YAML-formatted dates
-
1
DATE_REGEX = /^(?:\d{4}-\d{2}-\d{2}|\d{4}-\d{1,2}-\d{1,2}[T \t]+\d{1,2}:\d{2}:\d{2}(\.[0-9]*)?(([ \t]*)Z|[-+]\d{2}?(:\d{2})?))$/
-
-
# Dumps objects in JSON (JavaScript Object Notation).
-
# See www.json.org for more info.
-
#
-
# ActiveSupport::JSON.encode({ team: 'rails', players: '36' })
-
# # => "{\"team\":\"rails\",\"players\":\"36\"}"
-
1
def self.encode(value, options = nil)
-
Encoding::Encoder.new(options).encode(value)
-
end
-
-
1
module Encoding #:nodoc:
-
1
class CircularReferenceError < StandardError; end
-
-
1
class Encoder
-
1
attr_reader :options
-
-
1
def initialize(options = nil)
-
@options = options || {}
-
@seen = Set.new
-
end
-
-
1
def encode(value, use_options = true)
-
check_for_circular_references(value) do
-
jsonified = use_options ? value.as_json(options_for(value)) : value.as_json
-
jsonified.encode_json(self)
-
end
-
end
-
-
# like encode, but only calls as_json, without encoding to string.
-
1
def as_json(value, use_options = true)
-
check_for_circular_references(value) do
-
use_options ? value.as_json(options_for(value)) : value.as_json
-
end
-
end
-
-
1
def options_for(value)
-
if value.is_a?(Array) || value.is_a?(Hash)
-
# hashes and arrays need to get encoder in the options, so that
-
# they can detect circular references.
-
options.merge(:encoder => self)
-
else
-
options
-
end
-
end
-
-
1
def escape(string)
-
Encoding.escape(string)
-
end
-
-
1
private
-
1
def check_for_circular_references(value)
-
unless @seen.add?(value.__id__)
-
raise CircularReferenceError, 'object references itself'
-
end
-
yield
-
ensure
-
@seen.delete(value.__id__)
-
end
-
end
-
-
-
1
ESCAPED_CHARS = {
-
"\x00" => '\u0000', "\x01" => '\u0001', "\x02" => '\u0002',
-
"\x03" => '\u0003', "\x04" => '\u0004', "\x05" => '\u0005',
-
"\x06" => '\u0006', "\x07" => '\u0007', "\x0B" => '\u000B',
-
"\x0E" => '\u000E', "\x0F" => '\u000F', "\x10" => '\u0010',
-
"\x11" => '\u0011', "\x12" => '\u0012', "\x13" => '\u0013',
-
"\x14" => '\u0014', "\x15" => '\u0015', "\x16" => '\u0016',
-
"\x17" => '\u0017', "\x18" => '\u0018', "\x19" => '\u0019',
-
"\x1A" => '\u001A', "\x1B" => '\u001B', "\x1C" => '\u001C',
-
"\x1D" => '\u001D', "\x1E" => '\u001E', "\x1F" => '\u001F',
-
"\010" => '\b',
-
"\f" => '\f',
-
"\n" => '\n',
-
"\r" => '\r',
-
"\t" => '\t',
-
'"' => '\"',
-
'\\' => '\\\\',
-
'>' => '\u003E',
-
'<' => '\u003C',
-
'&' => '\u0026' }
-
-
1
class << self
-
# If true, use ISO 8601 format for dates and times. Otherwise, fall back
-
# to the Active Support legacy format.
-
1
attr_accessor :use_standard_json_time_format
-
-
# If false, serializes BigDecimal objects as numeric instead of wrapping
-
# them in a string.
-
1
attr_accessor :encode_big_decimal_as_string
-
-
1
attr_accessor :escape_regex
-
1
attr_reader :escape_html_entities_in_json
-
-
1
def escape_html_entities_in_json=(value)
-
1
self.escape_regex = \
-
if @escape_html_entities_in_json = value
-
1
/[\x00-\x1F"\\><&]/
-
else
-
/[\x00-\x1F"\\]/
-
end
-
end
-
-
1
def escape(string)
-
string = string.encode(::Encoding::UTF_8, :undef => :replace).force_encoding(::Encoding::BINARY)
-
json = string.
-
gsub(escape_regex) { |s| ESCAPED_CHARS[s] }.
-
gsub(/([\xC0-\xDF][\x80-\xBF]|
-
[\xE0-\xEF][\x80-\xBF]{2}|
-
[\xF0-\xF7][\x80-\xBF]{3})+/nx) { |s|
-
s.unpack("U*").pack("n*").unpack("H*")[0].gsub(/.{4}/n, '\\\\u\&')
-
}
-
json = %("#{json}")
-
json.force_encoding(::Encoding::UTF_8)
-
json
-
end
-
end
-
-
1
self.use_standard_json_time_format = true
-
1
self.escape_html_entities_in_json = true
-
1
self.encode_big_decimal_as_string = true
-
end
-
end
-
end
-
-
1
class Object
-
1
def as_json(options = nil) #:nodoc:
-
if respond_to?(:to_hash)
-
to_hash
-
else
-
instance_values
-
end
-
end
-
end
-
-
1
class Struct #:nodoc:
-
1
def as_json(options = nil)
-
Hash[members.zip(values)]
-
end
-
end
-
-
1
class TrueClass
-
1
def as_json(options = nil) #:nodoc:
-
self
-
end
-
-
1
def encode_json(encoder) #:nodoc:
-
to_s
-
end
-
end
-
-
1
class FalseClass
-
1
def as_json(options = nil) #:nodoc:
-
self
-
end
-
-
1
def encode_json(encoder) #:nodoc:
-
to_s
-
end
-
end
-
-
1
class NilClass
-
1
def as_json(options = nil) #:nodoc:
-
self
-
end
-
-
1
def encode_json(encoder) #:nodoc:
-
'null'
-
end
-
end
-
-
1
class String
-
1
def as_json(options = nil) #:nodoc:
-
self
-
end
-
-
1
def encode_json(encoder) #:nodoc:
-
encoder.escape(self)
-
end
-
end
-
-
1
class Symbol
-
1
def as_json(options = nil) #:nodoc:
-
to_s
-
end
-
end
-
-
1
class Numeric
-
1
def as_json(options = nil) #:nodoc:
-
self
-
end
-
-
1
def encode_json(encoder) #:nodoc:
-
to_s
-
end
-
end
-
-
1
class Float
-
# Encoding Infinity or NaN to JSON should return "null". The default returns
-
# "Infinity" or "NaN" which breaks parsing the JSON. E.g. JSON.parse('[NaN]').
-
1
def as_json(options = nil) #:nodoc:
-
finite? ? self : nil
-
end
-
end
-
-
1
class BigDecimal
-
# A BigDecimal would be naturally represented as a JSON number. Most libraries,
-
# however, parse non-integer JSON numbers directly as floats. Clients using
-
# those libraries would get in general a wrong number and no way to recover
-
# other than manually inspecting the string with the JSON code itself.
-
#
-
# That's why a JSON string is returned. The JSON literal is not numeric, but
-
# if the other end knows by contract that the data is supposed to be a
-
# BigDecimal, it still has the chance to post-process the string and get the
-
# real value.
-
#
-
# Use <tt>ActiveSupport.use_standard_json_big_decimal_format = true</tt> to
-
# override this behaviour.
-
1
def as_json(options = nil) #:nodoc:
-
if finite?
-
ActiveSupport.encode_big_decimal_as_string ? to_s : self
-
else
-
nil
-
end
-
end
-
end
-
-
1
class Regexp
-
1
def as_json(options = nil) #:nodoc:
-
to_s
-
end
-
end
-
-
1
module Enumerable
-
1
def as_json(options = nil) #:nodoc:
-
to_a.as_json(options)
-
end
-
end
-
-
1
class Range
-
1
def as_json(options = nil) #:nodoc:
-
to_s
-
end
-
end
-
-
1
class Array
-
1
def as_json(options = nil) #:nodoc:
-
# use encoder as a proxy to call as_json on all elements, to protect from circular references
-
encoder = options && options[:encoder] || ActiveSupport::JSON::Encoding::Encoder.new(options)
-
map { |v| encoder.as_json(v, options) }
-
end
-
-
1
def encode_json(encoder) #:nodoc:
-
# we assume here that the encoder has already run as_json on self and the elements, so we run encode_json directly
-
"[#{map { |v| v.encode_json(encoder) } * ','}]"
-
end
-
end
-
-
1
class Hash
-
1
def as_json(options = nil) #:nodoc:
-
# create a subset of the hash by applying :only or :except
-
subset = if options
-
if attrs = options[:only]
-
slice(*Array(attrs))
-
elsif attrs = options[:except]
-
except(*Array(attrs))
-
else
-
self
-
end
-
else
-
self
-
end
-
-
# use encoder as a proxy to call as_json on all values in the subset, to protect from circular references
-
encoder = options && options[:encoder] || ActiveSupport::JSON::Encoding::Encoder.new(options)
-
Hash[subset.map { |k, v| [k.to_s, encoder.as_json(v, options)] }]
-
end
-
-
1
def encode_json(encoder) #:nodoc:
-
# values are encoded with use_options = false, because we don't want hash representations from ActiveModel to be
-
# processed once again with as_json with options, as this could cause unexpected results (i.e. missing fields);
-
-
# on the other hand, we need to run as_json on the elements, because the model representation may contain fields
-
# like Time/Date in their original (not jsonified) form, etc.
-
-
"{#{map { |k,v| "#{encoder.encode(k.to_s)}:#{encoder.encode(v, false)}" } * ','}}"
-
end
-
end
-
-
1
class Time
-
1
def as_json(options = nil) #:nodoc:
-
if ActiveSupport.use_standard_json_time_format
-
xmlschema
-
else
-
%(#{strftime("%Y/%m/%d %H:%M:%S")} #{formatted_offset(false)})
-
end
-
end
-
end
-
-
1
class Date
-
1
def as_json(options = nil) #:nodoc:
-
if ActiveSupport.use_standard_json_time_format
-
strftime("%Y-%m-%d")
-
else
-
strftime("%Y/%m/%d")
-
end
-
end
-
end
-
-
1
class DateTime
-
1
def as_json(options = nil) #:nodoc:
-
if ActiveSupport.use_standard_json_time_format
-
xmlschema
-
else
-
strftime('%Y/%m/%d %H:%M:%S %z')
-
end
-
end
-
end
-
1
require 'active_support/deprecation'
-
-
1
module ActiveSupport
-
1
module JSON
-
# Deprecated: A string that returns itself as its JSON-encoded form.
-
1
class Variable < String
-
1
def initialize(*args)
-
message = 'ActiveSupport::JSON::Variable is deprecated and will be removed in Rails 4.1. ' \
-
'For your own custom JSON literals, define #as_json and #encode_json yourself.'
-
ActiveSupport::Deprecation.warn message
-
super
-
end
-
-
1
def as_json(options = nil) self end #:nodoc:
-
1
def encode_json(encoder) self end #:nodoc:
-
end
-
end
-
end
-
1
module ActiveSupport
-
# lazy_load_hooks allows rails to lazily load a lot of components and thus
-
# making the app boot faster. Because of this feature now there is no need to
-
# require <tt>ActiveRecord::Base</tt> at boot time purely to apply
-
# configuration. Instead a hook is registered that applies configuration once
-
# <tt>ActiveRecord::Base</tt> is loaded. Here <tt>ActiveRecord::Base</tt> is
-
# used as example but this feature can be applied elsewhere too.
-
#
-
# Here is an example where +on_load+ method is called to register a hook.
-
#
-
# initializer 'active_record.initialize_timezone' do
-
# ActiveSupport.on_load(:active_record) do
-
# self.time_zone_aware_attributes = true
-
# self.default_timezone = :utc
-
# end
-
# end
-
#
-
# When the entirety of +activerecord/lib/active_record/base.rb+ has been
-
# evaluated then +run_load_hooks+ is invoked. The very last line of
-
# +activerecord/lib/active_record/base.rb+ is:
-
#
-
# ActiveSupport.run_load_hooks(:active_record, ActiveRecord::Base)
-
5
@load_hooks = Hash.new { |h,k| h[k] = [] }
-
5
@loaded = Hash.new { |h,k| h[k] = [] }
-
-
1
def self.on_load(name, options = {}, &block)
-
4
@loaded[name].each do |base|
-
3
execute_hook(base, options, block)
-
end
-
-
4
@load_hooks[name] << [block, options]
-
end
-
-
1
def self.execute_hook(base, options, block)
-
4
if options[:yield]
-
block.call(base)
-
else
-
4
base.instance_eval(&block)
-
end
-
end
-
-
1
def self.run_load_hooks(name, base = Object)
-
4
@loaded[name] << base
-
4
@load_hooks[name].each do |hook, options|
-
1
execute_hook(base, options, hook)
-
end
-
end
-
end
-
1
require 'active_support/core_ext/module/attribute_accessors'
-
1
require 'active_support/core_ext/class/attribute'
-
-
1
module ActiveSupport
-
# ActiveSupport::LogSubscriber is an object set to consume
-
# ActiveSupport::Notifications with the sole purpose of logging them.
-
# The log subscriber dispatches notifications to a registered object based
-
# on its given namespace.
-
#
-
# An example would be Active Record log subscriber responsible for logging
-
# queries:
-
#
-
# module ActiveRecord
-
# class LogSubscriber < ActiveSupport::LogSubscriber
-
# def sql(event)
-
# "#{event.payload[:name]} (#{event.duration}) #{event.payload[:sql]}"
-
# end
-
# end
-
# end
-
#
-
# And it's finally registered as:
-
#
-
# ActiveRecord::LogSubscriber.attach_to :active_record
-
#
-
# Since we need to know all instance methods before attaching the log
-
# subscriber, the line above should be called after your
-
# <tt>ActiveRecord::LogSubscriber</tt> definition.
-
#
-
# After configured, whenever a "sql.active_record" notification is published,
-
# it will properly dispatch the event (ActiveSupport::Notifications::Event) to
-
# the sql method.
-
#
-
# Log subscriber also has some helpers to deal with logging and automatically
-
# flushes all logs when the request finishes (via action_dispatch.callback
-
# notification) in a Rails environment.
-
1
class LogSubscriber
-
# Embed in a String to clear all previous ANSI sequences.
-
1
CLEAR = "\e[0m"
-
1
BOLD = "\e[1m"
-
-
# Colors
-
1
BLACK = "\e[30m"
-
1
RED = "\e[31m"
-
1
GREEN = "\e[32m"
-
1
YELLOW = "\e[33m"
-
1
BLUE = "\e[34m"
-
1
MAGENTA = "\e[35m"
-
1
CYAN = "\e[36m"
-
1
WHITE = "\e[37m"
-
-
1
mattr_accessor :colorize_logging
-
1
self.colorize_logging = true
-
-
1
class << self
-
1
def logger
-
@logger ||= Rails.logger if defined?(Rails)
-
@logger
-
end
-
-
1
attr_writer :logger
-
-
1
def attach_to(namespace, log_subscriber=new, notifier=ActiveSupport::Notifications)
-
5
log_subscribers << log_subscriber
-
-
5
log_subscriber.public_methods(false).each do |event|
-
26
next if %w{ start finish }.include?(event.to_s)
-
-
26
notifier.subscribe("#{event}.#{namespace}", log_subscriber)
-
end
-
end
-
-
1
def log_subscribers
-
5
@@log_subscribers ||= []
-
end
-
-
# Flush all log_subscribers' logger.
-
1
def flush_all!
-
logger.flush if logger.respond_to?(:flush)
-
end
-
end
-
-
1
def initialize
-
5
@queue_key = [self.class.name, object_id].join "-"
-
5
super
-
end
-
-
1
def logger
-
LogSubscriber.logger
-
end
-
-
1
def start(name, id, payload)
-
41
return unless logger
-
-
2
e = ActiveSupport::Notifications::Event.new(name, Time.now, nil, id, payload)
-
2
parent = event_stack.last
-
2
parent << e if parent
-
-
2
event_stack.push e
-
end
-
-
1
def finish(name, id, payload)
-
41
return unless logger
-
-
2
finished = Time.now
-
2
event = event_stack.pop
-
2
event.end = finished
-
2
event.payload.merge!(payload)
-
-
2
method = name.split('.').first
-
2
begin
-
2
send(method, event)
-
rescue Exception => e
-
logger.error "Could not log #{name.inspect} event. #{e.class}: #{e.message} #{e.backtrace}"
-
end
-
end
-
-
1
protected
-
-
1
%w(info debug warn error fatal unknown).each do |level|
-
6
class_eval <<-METHOD, __FILE__, __LINE__ + 1
-
def #{level}(progname = nil, &block)
-
logger.#{level}(progname, &block) if logger
-
end
-
METHOD
-
end
-
-
# Set color by using a string or one of the defined constants. If a third
-
# option is set to +true+, it also adds bold to the string. This is based
-
# on the Highline implementation and will automatically append CLEAR to the
-
# end of the returned String.
-
1
def color(text, color, bold=false)
-
return text unless colorize_logging
-
color = self.class.const_get(color.upcase) if color.is_a?(Symbol)
-
bold = bold ? BOLD : ""
-
"#{bold}#{color}#{text}#{CLEAR}"
-
end
-
-
1
private
-
-
1
def event_stack
-
6
Thread.current[@queue_key] ||= []
-
end
-
end
-
end
-
1
require 'simplecov'
-
1
SimpleCov.start
-
1
require 'active_support/log_subscriber'
-
1
require 'active_support/buffered_logger'
-
1
require 'active_support/notifications'
-
-
1
module ActiveSupport
-
1
class LogSubscriber
-
# Provides some helpers to deal with testing log subscribers by setting up
-
# notifications. Take for instance Active Record subscriber tests:
-
#
-
# class SyncLogSubscriberTest < ActiveSupport::TestCase
-
# include ActiveSupport::LogSubscriber::TestHelper
-
#
-
# def setup
-
# ActiveRecord::LogSubscriber.attach_to(:active_record)
-
# end
-
#
-
# def test_basic_query_logging
-
# Developer.all
-
# wait
-
# assert_equal 1, @logger.logged(:debug).size
-
# assert_match(/Developer Load/, @logger.logged(:debug).last)
-
# assert_match(/SELECT \* FROM "developers"/, @logger.logged(:debug).last)
-
# end
-
# end
-
#
-
# All you need to do is to ensure that your log subscriber is added to
-
# Rails::Subscriber, as in the second line of the code above. The test
-
# helpers are responsible for setting up the queue, subscriptions and
-
# turning colors in logs off.
-
#
-
# The messages are available in the @logger instance, which is a logger with
-
# limited powers (it actually does not send anything to your output), and
-
# you can collect them doing @logger.logged(level), where level is the level
-
# used in logging, like info, debug, warn and so on.
-
1
module TestHelper
-
1
def setup
-
2
@logger = MockLogger.new
-
2
@notifier = ActiveSupport::Notifications::Fanout.new
-
-
2
ActiveSupport::LogSubscriber.colorize_logging = false
-
-
2
@old_notifier = ActiveSupport::Notifications.notifier
-
2
set_logger(@logger)
-
2
ActiveSupport::Notifications.notifier = @notifier
-
end
-
-
1
def teardown
-
2
set_logger(nil)
-
2
ActiveSupport::Notifications.notifier = @old_notifier
-
end
-
-
1
class MockLogger
-
1
include ActiveSupport::Logger::Severity
-
-
1
attr_reader :flush_count
-
1
attr_accessor :level
-
-
1
def initialize(level = DEBUG)
-
2
@flush_count = 0
-
2
@level = level
-
6
@logged = Hash.new { |h,k| h[k] = [] }
-
end
-
-
1
def method_missing(level, message = nil)
-
4
if block_given?
-
@logged[level] << yield
-
else
-
4
@logged[level] << message
-
end
-
end
-
-
1
def logged(level)
-
16
@logged[level].compact.map { |l| l.to_s.strip }
-
end
-
-
1
def flush
-
@flush_count += 1
-
end
-
-
1
ActiveSupport::Logger::Severity.constants.each do |severity|
-
6
class_eval <<-EOT, __FILE__, __LINE__ + 1
-
def #{severity.downcase}?
-
#{severity} >= @level
-
end
-
EOT
-
end
-
end
-
-
# Wait notifications to be published.
-
1
def wait
-
2
@notifier.wait
-
end
-
-
# Overwrite if you use another logger in your log subscriber.
-
#
-
# def logger
-
# ActiveRecord::Base.logger = @logger
-
# end
-
1
def set_logger(logger)
-
ActiveSupport::LogSubscriber.logger = logger
-
end
-
end
-
end
-
end
-
1
require 'logger'
-
-
1
module ActiveSupport
-
1
class Logger < ::Logger
-
# Broadcasts logs to multiple loggers.
-
1
def self.broadcast(logger) # :nodoc:
-
Module.new do
-
define_method(:add) do |*args, &block|
-
logger.add(*args, &block)
-
super(*args, &block)
-
end
-
-
define_method(:<<) do |x|
-
logger << x
-
super(x)
-
end
-
-
define_method(:close) do
-
logger.close
-
super()
-
end
-
-
define_method(:progname=) do |name|
-
logger.progname = name
-
super(name)
-
end
-
-
define_method(:formatter=) do |formatter|
-
logger.formatter = formatter
-
super(formatter)
-
end
-
-
define_method(:level=) do |level|
-
logger.level = level
-
super(level)
-
end
-
end
-
end
-
-
1
def initialize(*args)
-
2
super
-
2
@formatter = SimpleFormatter.new
-
end
-
-
# Simple formatter which only displays the message.
-
1
class SimpleFormatter < ::Logger::Formatter
-
# This method is invoked when a log event occurs
-
1
def call(severity, timestamp, progname, msg)
-
"#{String === msg ? msg : msg.inspect}\n"
-
end
-
end
-
end
-
end
-
1
module ActiveSupport #:nodoc:
-
1
module Multibyte
-
1
autoload :Chars, 'active_support/multibyte/chars'
-
1
autoload :Unicode, 'active_support/multibyte/unicode'
-
-
# The proxy class returned when calling mb_chars. You can use this accessor
-
# to configure your own proxy class so you can support other encodings. See
-
# the ActiveSupport::Multibyte::Chars implementation for an example how to
-
# do this.
-
#
-
# ActiveSupport::Multibyte.proxy_class = CharsForUTF32
-
1
def self.proxy_class=(klass)
-
@proxy_class = klass
-
end
-
-
# Returns the current proxy class.
-
1
def self.proxy_class
-
72
@proxy_class ||= ActiveSupport::Multibyte::Chars
-
end
-
end
-
end
-
# encoding: utf-8
-
1
require 'active_support/json'
-
1
require 'active_support/core_ext/string/access'
-
1
require 'active_support/core_ext/string/behavior'
-
1
require 'active_support/core_ext/module/delegation'
-
-
1
module ActiveSupport #:nodoc:
-
1
module Multibyte #:nodoc:
-
# Chars enables you to work transparently with UTF-8 encoding in the Ruby
-
# String class without having extensive knowledge about the encoding. A
-
# Chars object accepts a string upon initialization and proxies String
-
# methods in an encoding safe manner. All the normal String methods are also
-
# implemented on the proxy.
-
#
-
# String methods are proxied through the Chars object, and can be accessed
-
# through the +mb_chars+ method. Methods which would normally return a
-
# String object now return a Chars object so methods can be chained.
-
#
-
# 'The Perfect String '.mb_chars.downcase.strip.normalize # => "the perfect string"
-
#
-
# Chars objects are perfectly interchangeable with String objects as long as
-
# no explicit class checks are made. If certain methods do explicitly check
-
# the class, call +to_s+ before you pass chars objects to them.
-
#
-
# bad.explicit_checking_method 'T'.mb_chars.downcase.to_s
-
#
-
# The default Chars implementation assumes that the encoding of the string
-
# is UTF-8, if you want to handle different encodings you can write your own
-
# multibyte string handler and configure it through
-
# ActiveSupport::Multibyte.proxy_class.
-
#
-
# class CharsForUTF32
-
# def size
-
# @wrapped_string.size / 4
-
# end
-
#
-
# def self.accepts?(string)
-
# string.length % 4 == 0
-
# end
-
# end
-
#
-
# ActiveSupport::Multibyte.proxy_class = CharsForUTF32
-
1
class Chars
-
1
include Comparable
-
1
attr_reader :wrapped_string
-
1
alias to_s wrapped_string
-
1
alias to_str wrapped_string
-
-
1
delegate :<=>, :=~, :acts_like_string?, :to => :wrapped_string
-
-
# Creates a new Chars instance by wrapping _string_.
-
1
def initialize(string)
-
60
@wrapped_string = string
-
60
@wrapped_string.force_encoding(Encoding::UTF_8) unless @wrapped_string.frozen?
-
end
-
-
# Forward all undefined methods to the wrapped string.
-
1
def method_missing(method, *args, &block)
-
39
if method.to_s =~ /!$/
-
result = @wrapped_string.__send__(method, *args, &block)
-
self if result
-
else
-
39
result = @wrapped_string.__send__(method, *args, &block)
-
39
result.kind_of?(String) ? chars(result) : result
-
end
-
end
-
-
# Returns +true+ if _obj_ responds to the given method. Private methods
-
# are included in the search only if the optional second parameter
-
# evaluates to +true+.
-
1
def respond_to_missing?(method, include_private)
-
3
@wrapped_string.respond_to?(method, include_private)
-
end
-
-
# Returns +true+ when the proxy class can handle the string. Returns
-
# +false+ otherwise.
-
1
def self.consumes?(string)
-
36
string.encoding == Encoding::UTF_8
-
end
-
-
# Works just like <tt>String#split</tt>, with the exception that the items
-
# in the resulting list are Chars instances instead of String. This makes
-
# chaining methods easier.
-
#
-
# 'Café périferôl'.mb_chars.split(/é/).map { |part| part.upcase.to_s } # => ["CAF", " P", "RIFERÔL"]
-
1
def split(*args)
-
@wrapped_string.split(*args).map { |i| self.class.new(i) }
-
end
-
-
# Works like like <tt>String#slice!</tt>, but returns an instance of
-
# Chars, or nil if the string was not modified.
-
1
def slice!(*args)
-
chars(@wrapped_string.slice!(*args))
-
end
-
-
# Reverses all characters in the string.
-
#
-
# 'Café'.mb_chars.reverse.to_s # => 'éfaC'
-
1
def reverse
-
chars(Unicode.unpack_graphemes(@wrapped_string).reverse.flatten.pack('U*'))
-
end
-
-
# Limits the byte size of the string to a number of bytes without breaking
-
# characters. Usable when the storage for a string is limited for some
-
# reason.
-
#
-
# 'こんにちは'.mb_chars.limit(7).to_s # => "こん"
-
1
def limit(limit)
-
slice(0...translate_offset(limit))
-
end
-
-
# Converts characters in the string to uppercase.
-
#
-
# 'Laurent, où sont les tests ?'.mb_chars.upcase.to_s # => "LAURENT, OÙ SONT LES TESTS ?"
-
1
def upcase
-
chars Unicode.upcase(@wrapped_string)
-
end
-
-
# Converts characters in the string to lowercase.
-
#
-
# 'VĚDA A VÝZKUM'.mb_chars.downcase.to_s # => "věda a výzkum"
-
1
def downcase
-
chars Unicode.downcase(@wrapped_string)
-
end
-
-
# Converts characters in the string to the opposite case.
-
#
-
# 'El Cañón".mb_chars.swapcase.to_s # => "eL cAÑÓN"
-
1
def swapcase
-
chars Unicode.swapcase(@wrapped_string)
-
end
-
-
# Converts the first character to uppercase and the remainder to lowercase.
-
#
-
# 'über'.mb_chars.capitalize.to_s # => "Über"
-
1
def capitalize
-
(slice(0) || chars('')).upcase + (slice(1..-1) || chars('')).downcase
-
end
-
-
# Capitalizes the first letter of every word, when possible.
-
#
-
# "ÉL QUE SE ENTERÓ".mb_chars.titleize # => "Él Que Se Enteró"
-
# "日本語".mb_chars.titleize # => "日本語"
-
1
def titleize
-
chars(downcase.to_s.gsub(/\b('?\S)/u) { Unicode.upcase($1)})
-
end
-
1
alias_method :titlecase, :titleize
-
-
# Returns the KC normalization of the string by default. NFKC is
-
# considered the best normalization form for passing strings to databases
-
# and validations.
-
#
-
# * <tt>form</tt> - The form you want to normalize in. Should be one of the following:
-
# <tt>:c</tt>, <tt>:kc</tt>, <tt>:d</tt>, or <tt>:kd</tt>. Default is
-
# ActiveSupport::Multibyte::Unicode.default_normalization_form
-
1
def normalize(form = nil)
-
chars(Unicode.normalize(@wrapped_string, form))
-
end
-
-
# Performs canonical decomposition on all the characters.
-
#
-
# 'é'.length # => 2
-
# 'é'.mb_chars.decompose.to_s.length # => 3
-
1
def decompose
-
chars(Unicode.decompose(:canonical, @wrapped_string.codepoints.to_a).pack('U*'))
-
end
-
-
# Performs composition on all the characters.
-
#
-
# 'é'.length # => 3
-
# 'é'.mb_chars.compose.to_s.length # => 2
-
1
def compose
-
chars(Unicode.compose(@wrapped_string.codepoints.to_a).pack('U*'))
-
end
-
-
# Returns the number of grapheme clusters in the string.
-
#
-
# 'क्षि'.mb_chars.length # => 4
-
# 'क्षि'.mb_chars.grapheme_length # => 3
-
1
def grapheme_length
-
Unicode.unpack_graphemes(@wrapped_string).length
-
end
-
-
# Replaces all ISO-8859-1 or CP1252 characters by their UTF-8 equivalent
-
# resulting in a valid UTF-8 string.
-
#
-
# Passing +true+ will forcibly tidy all bytes, assuming that the string's
-
# encoding is entirely CP1252 or ISO-8859-1.
-
1
def tidy_bytes(force = false)
-
chars(Unicode.tidy_bytes(@wrapped_string, force))
-
end
-
-
1
def as_json(options = nil) #:nodoc:
-
to_s.as_json(options)
-
end
-
-
1
%w(capitalize downcase reverse tidy_bytes upcase).each do |method|
-
5
define_method("#{method}!") do |*args|
-
@wrapped_string = send(method, *args).to_s
-
self
-
end
-
end
-
-
1
protected
-
-
1
def translate_offset(byte_offset) #:nodoc:
-
return nil if byte_offset.nil?
-
return 0 if @wrapped_string == ''
-
-
begin
-
@wrapped_string.byteslice(0...byte_offset).unpack('U*').length
-
rescue ArgumentError
-
byte_offset -= 1
-
retry
-
end
-
end
-
-
1
def chars(string) #:nodoc:
-
24
self.class.new(string)
-
end
-
end
-
end
-
end
-
1
require 'active_support/notifications/instrumenter'
-
1
require 'active_support/notifications/fanout'
-
-
1
module ActiveSupport
-
# = Notifications
-
#
-
# <tt>ActiveSupport::Notifications</tt> provides an instrumentation API for
-
# Ruby.
-
#
-
# == Instrumenters
-
#
-
# To instrument an event you just need to do:
-
#
-
# ActiveSupport::Notifications.instrument('render', extra: :information) do
-
# render text: 'Foo'
-
# end
-
#
-
# That executes the block first and notifies all subscribers once done.
-
#
-
# In the example above +render+ is the name of the event, and the rest is called
-
# the _payload_. The payload is a mechanism that allows instrumenters to pass
-
# extra information to subscribers. Payloads consist of a hash whose contents
-
# are arbitrary and generally depend on the event.
-
#
-
# == Subscribers
-
#
-
# You can consume those events and the information they provide by registering
-
# a subscriber.
-
#
-
# ActiveSupport::Notifications.subscribe('render') do |name, start, finish, id, payload|
-
# name # => String, name of the event (such as 'render' from above)
-
# start # => Time, when the instrumented block started execution
-
# finish # => Time, when the instrumented block ended execution
-
# id # => String, unique ID for this notification
-
# payload # => Hash, the payload
-
# end
-
#
-
# For instance, let's store all "render" events in an array:
-
#
-
# events = []
-
#
-
# ActiveSupport::Notifications.subscribe('render') do |*args|
-
# events << ActiveSupport::Notifications::Event.new(*args)
-
# end
-
#
-
# That code returns right away, you are just subscribing to "render" events.
-
# The block is saved and will be called whenever someone instruments "render":
-
#
-
# ActiveSupport::Notifications.instrument('render', extra: :information) do
-
# render text: 'Foo'
-
# end
-
#
-
# event = events.first
-
# event.name # => "render"
-
# event.duration # => 10 (in milliseconds)
-
# event.payload # => { extra: :information }
-
#
-
# The block in the <tt>subscribe</tt> call gets the name of the event, start
-
# timestamp, end timestamp, a string with a unique identifier for that event
-
# (something like "535801666f04d0298cd6"), and a hash with the payload, in
-
# that order.
-
#
-
# If an exception happens during that particular instrumentation the payload will
-
# have a key <tt>:exception</tt> with an array of two elements as value: a string with
-
# the name of the exception class, and the exception message.
-
#
-
# As the previous example depicts, the class <tt>ActiveSupport::Notifications::Event</tt>
-
# is able to take the arguments as they come and provide an object-oriented
-
# interface to that data.
-
#
-
# It is also possible to pass an object as the second parameter passed to the
-
# <tt>subscribe</tt> method instead of a block:
-
#
-
# module ActionController
-
# class PageRequest
-
# def call(name, started, finished, unique_id, payload)
-
# Rails.logger.debug ['notification:', name, started, finished, unique_id, payload].join(' ')
-
# end
-
# end
-
# end
-
#
-
# ActiveSupport::Notifications.subscribe('process_action.action_controller', ActionController::PageRequest.new)
-
#
-
# resulting in the following output within the logs including a hash with the payload:
-
#
-
# notification: process_action.action_controller 2012-04-13 01:08:35 +0300 2012-04-13 01:08:35 +0300 af358ed7fab884532ec7 {
-
# :controller=>"Devise::SessionsController",
-
# :action=>"new",
-
# :params=>{"action"=>"new", "controller"=>"devise/sessions"},
-
# :format=>:html,
-
# :method=>"GET",
-
# :path=>"/login/sign_in",
-
# :status=>200,
-
# :view_runtime=>279.3080806732178,
-
# :db_runtime=>40.053
-
# }
-
#
-
# You can also subscribe to all events whose name matches a certain regexp:
-
#
-
# ActiveSupport::Notifications.subscribe(/render/) do |*args|
-
# ...
-
# end
-
#
-
# and even pass no argument to <tt>subscribe</tt>, in which case you are subscribing
-
# to all events.
-
#
-
# == Temporary Subscriptions
-
#
-
# Sometimes you do not want to subscribe to an event for the entire life of
-
# the application. There are two ways to unsubscribe.
-
#
-
# WARNING: The instrumentation framework is designed for long-running subscribers,
-
# use this feature sparingly because it wipes some internal caches and that has
-
# a negative impact on performance.
-
#
-
# === Subscribe While a Block Runs
-
#
-
# You can subscribe to some event temporarily while some block runs. For
-
# example, in
-
#
-
# callback = lambda {|*args| ... }
-
# ActiveSupport::Notifications.subscribed(callback, "sql.active_record") do
-
# ...
-
# end
-
#
-
# the callback will be called for all "sql.active_record" events instrumented
-
# during the execution of the block. The callback is unsubscribed automatically
-
# after that.
-
#
-
# === Manual Unsubscription
-
#
-
# The +subscribe+ method returns a subscriber object:
-
#
-
# subscriber = ActiveSupport::Notifications.subscribe("render") do |*args|
-
# ...
-
# end
-
#
-
# To prevent that block from being called anymore, just unsubscribe passing
-
# that reference:
-
#
-
# ActiveSupport::Notifications.unsubscribe(subscriber)
-
#
-
# == Default Queue
-
#
-
# Notifications ships with a queue implementation that consumes and publish events
-
# to log subscribers in a thread. You can use any queue implementation you want.
-
#
-
1
module Notifications
-
1
class << self
-
1
attr_accessor :notifier
-
-
1
def publish(name, *args)
-
notifier.publish(name, *args)
-
end
-
-
1
def instrument(name, payload = {})
-
309
if notifier.listening?(name)
-
84
instrumenter.instrument(name, payload) { yield payload if block_given? }
-
else
-
267
yield payload if block_given?
-
end
-
end
-
-
1
def subscribe(*args, &block)
-
28
notifier.subscribe(*args, &block)
-
end
-
-
1
def subscribed(callback, *args, &block)
-
subscriber = subscribe(*args, &callback)
-
yield
-
ensure
-
unsubscribe(subscriber)
-
end
-
-
1
def unsubscribe(args)
-
2
notifier.unsubscribe(args)
-
end
-
-
1
def instrumenter
-
42
Thread.current[:"instrumentation_#{notifier.object_id}"] ||= Instrumenter.new(notifier)
-
end
-
end
-
-
1
self.notifier = Fanout.new
-
end
-
end
-
1
require 'mutex_m'
-
-
1
module ActiveSupport
-
1
module Notifications
-
# This is a default queue implementation that ships with Notifications.
-
# It just pushes events to all registered log subscribers.
-
#
-
# This class is thread safe. All methods are reentrant.
-
1
class Fanout
-
1
include Mutex_m
-
-
1
def initialize
-
3
@subscribers = []
-
3
@listeners_for = {}
-
3
super
-
end
-
-
1
def subscribe(pattern = nil, block = Proc.new)
-
28
subscriber = Subscribers.new pattern, block
-
28
synchronize do
-
28
@subscribers << subscriber
-
28
@listeners_for.clear
-
end
-
28
subscriber
-
end
-
-
1
def unsubscribe(subscriber)
-
2
synchronize do
-
44
@subscribers.reject! { |s| s.matches?(subscriber) }
-
2
@listeners_for.clear
-
end
-
end
-
-
1
def start(name, id, payload)
-
86
listeners_for(name).each { |s| s.start(name, id, payload) }
-
end
-
-
1
def finish(name, id, payload)
-
86
listeners_for(name).each { |s| s.finish(name, id, payload) }
-
end
-
-
1
def publish(name, *args)
-
listeners_for(name).each { |s| s.publish(name, *args) }
-
end
-
-
1
def listeners_for(name)
-
393
synchronize do
-
572
@listeners_for[name] ||= @subscribers.select { |s| s.subscribed_to?(name) }
-
end
-
end
-
-
1
def listening?(name)
-
309
listeners_for(name).any?
-
end
-
-
# This is a sync queue, so there is no waiting.
-
1
def wait
-
end
-
-
1
module Subscribers # :nodoc:
-
1
def self.new(pattern, listener)
-
28
if listener.respond_to?(:start) and listener.respond_to?(:finish)
-
26
subscriber = Evented.new pattern, listener
-
else
-
2
subscriber = Timed.new pattern, listener
-
end
-
-
28
unless pattern
-
AllMessages.new(subscriber)
-
else
-
28
subscriber
-
end
-
end
-
-
1
class Evented #:nodoc:
-
1
def initialize(pattern, delegate)
-
28
@pattern = pattern
-
28
@delegate = delegate
-
end
-
-
1
def start(name, id, payload)
-
41
@delegate.start name, id, payload
-
end
-
-
1
def finish(name, id, payload)
-
41
@delegate.finish name, id, payload
-
end
-
-
1
def subscribed_to?(name)
-
179
@pattern === name.to_s
-
end
-
-
1
def matches?(subscriber_or_name)
-
self === subscriber_or_name ||
-
42
@pattern && @pattern === subscriber_or_name
-
end
-
end
-
-
1
class Timed < Evented
-
1
def initialize(pattern, delegate)
-
2
@timestack = []
-
2
super
-
end
-
-
1
def publish(name, *args)
-
@delegate.call name, *args
-
end
-
-
1
def start(name, id, payload)
-
3
@timestack.push Time.now
-
end
-
-
1
def finish(name, id, payload)
-
3
started = @timestack.pop
-
3
@delegate.call(name, started, Time.now, id, payload)
-
end
-
end
-
-
1
class AllMessages # :nodoc:
-
1
def initialize(delegate)
-
@delegate = delegate
-
end
-
-
1
def start(name, id, payload)
-
@delegate.start name, id, payload
-
end
-
-
1
def finish(name, id, payload)
-
@delegate.finish name, id, payload
-
end
-
-
1
def publish(name, *args)
-
@delegate.publish name, *args
-
end
-
-
1
def subscribed_to?(name)
-
true
-
end
-
-
1
alias :matches? :===
-
end
-
end
-
end
-
end
-
end
-
1
require 'securerandom'
-
-
1
module ActiveSupport
-
1
module Notifications
-
# Instrumentors are stored in a thread local.
-
1
class Instrumenter
-
1
attr_reader :id
-
-
1
def initialize(notifier)
-
3
@id = unique_id
-
3
@notifier = notifier
-
end
-
-
# Instrument the given block by measuring the time taken to execute it
-
# and publish it. Notice that events get sent even if an error occurs
-
# in the passed-in block.
-
1
def instrument(name, payload={})
-
42
@notifier.start(name, @id, payload)
-
42
begin
-
42
yield
-
2
rescue Exception => e
-
2
payload[:exception] = [e.class.name, e.message]
-
2
raise e
-
ensure
-
42
@notifier.finish(name, @id, payload)
-
42
end
-
end
-
-
1
private
-
1
def unique_id
-
3
SecureRandom.hex(10)
-
end
-
end
-
-
1
class Event
-
1
attr_reader :name, :time, :transaction_id, :payload, :children
-
1
attr_accessor :end
-
-
1
def initialize(name, start, ending, transaction_id, payload)
-
2
@name = name
-
2
@payload = payload.dup
-
2
@time = start
-
2
@transaction_id = transaction_id
-
2
@end = ending
-
2
@children = []
-
end
-
-
1
def duration
-
2
1000.0 * (self.end - time)
-
end
-
-
1
def <<(event)
-
@children << event
-
end
-
-
1
def parent_of?(event)
-
@children.include? event
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/big_decimal/conversions'
-
1
require 'active_support/core_ext/object/blank'
-
1
require 'active_support/core_ext/hash/keys'
-
1
require 'active_support/i18n'
-
-
1
module ActiveSupport
-
1
module NumberHelper
-
1
extend self
-
-
1
DEFAULTS = {
-
# Used in number_to_delimited
-
# These are also the defaults for 'currency', 'percentage', 'precision', and 'human'
-
format: {
-
# Sets the separator between the units, for more precision (e.g. 1.0 / 2.0 == 0.5)
-
separator: ".",
-
# Delimits thousands (e.g. 1,000,000 is a million) (always in groups of three)
-
delimiter: ",",
-
# Number of decimals, behind the separator (the number 1 with a precision of 2 gives: 1.00)
-
precision: 3,
-
# If set to true, precision will mean the number of significant digits instead
-
# of the number of decimal digits (1234 with precision 2 becomes 1200, 1.23543 becomes 1.2)
-
significant: false,
-
# If set, the zeros after the decimal separator will always be stripped (eg.: 1.200 will be 1.2)
-
strip_insignificant_zeros: false
-
},
-
-
# Used in number_to_currency
-
currency: {
-
format: {
-
format: "%u%n",
-
negative_format: "-%u%n",
-
unit: "$",
-
# These five are to override number.format and are optional
-
separator: ".",
-
delimiter: ",",
-
precision: 2,
-
significant: false,
-
strip_insignificant_zeros: false
-
}
-
},
-
-
# Used in number_to_percentage
-
percentage: {
-
format: {
-
delimiter: "",
-
format: "%n%"
-
}
-
},
-
-
# Used in number_to_rounded
-
precision: {
-
format: {
-
delimiter: ""
-
}
-
},
-
-
# Used in number_to_human_size and number_to_human
-
human: {
-
format: {
-
# These five are to override number.format and are optional
-
delimiter: "",
-
precision: 3,
-
significant: true,
-
strip_insignificant_zeros: true
-
},
-
# Used in number_to_human_size
-
storage_units: {
-
# Storage units output formatting.
-
# %u is the storage unit, %n is the number (default: 2 MB)
-
format: "%n %u",
-
units: {
-
byte: "Bytes",
-
kb: "KB",
-
mb: "MB",
-
gb: "GB",
-
tb: "TB"
-
}
-
},
-
# Used in number_to_human
-
decimal_units: {
-
format: "%n %u",
-
# Decimal units output formatting
-
# By default we will only quantify some of the exponents
-
# but the commented ones might be defined or overridden
-
# by the user.
-
units: {
-
# femto: Quadrillionth
-
# pico: Trillionth
-
# nano: Billionth
-
# micro: Millionth
-
# mili: Thousandth
-
# centi: Hundredth
-
# deci: Tenth
-
unit: "",
-
# ten:
-
# one: Ten
-
# other: Tens
-
# hundred: Hundred
-
thousand: "Thousand",
-
million: "Million",
-
billion: "Billion",
-
trillion: "Trillion",
-
quadrillion: "Quadrillion"
-
}
-
}
-
}
-
}
-
-
1
DECIMAL_UNITS = { 0 => :unit, 1 => :ten, 2 => :hundred, 3 => :thousand, 6 => :million, 9 => :billion, 12 => :trillion, 15 => :quadrillion,
-
-1 => :deci, -2 => :centi, -3 => :mili, -6 => :micro, -9 => :nano, -12 => :pico, -15 => :femto }
-
-
1
STORAGE_UNITS = [:byte, :kb, :mb, :gb, :tb]
-
-
# Formats a +number+ into a US phone number (e.g., (555)
-
# 123-9876). You can customize the format in the +options+ hash.
-
#
-
# ==== Options
-
#
-
# * <tt>:area_code</tt> - Adds parentheses around the area code.
-
# * <tt>:delimiter</tt> - Specifies the delimiter to use
-
# (defaults to "-").
-
# * <tt>:extension</tt> - Specifies an extension to add to the
-
# end of the generated number.
-
# * <tt>:country_code</tt> - Sets the country code for the phone
-
# number.
-
# ==== Examples
-
#
-
# number_to_phone(5551234) # => 555-1234
-
# number_to_phone('5551234') # => 555-1234
-
# number_to_phone(1235551234) # => 123-555-1234
-
# number_to_phone(1235551234, area_code: true) # => (123) 555-1234
-
# number_to_phone(1235551234, delimiter: ' ') # => 123 555 1234
-
# number_to_phone(1235551234, area_code: true, extension: 555) # => (123) 555-1234 x 555
-
# number_to_phone(1235551234, country_code: 1) # => +1-123-555-1234
-
# number_to_phone('123a456') # => 123a456
-
#
-
# number_to_phone(1235551234, country_code: 1, extension: 1343, delimiter: '.')
-
# # => +1.123.555.1234 x 1343
-
1
def number_to_phone(number, options = {})
-
return unless number
-
options = options.symbolize_keys
-
-
number = number.to_s.strip
-
area_code = options[:area_code]
-
delimiter = options[:delimiter] || "-"
-
extension = options[:extension]
-
country_code = options[:country_code]
-
-
if area_code
-
number.gsub!(/(\d{1,3})(\d{3})(\d{4}$)/,"(\\1) \\2#{delimiter}\\3")
-
else
-
number.gsub!(/(\d{0,3})(\d{3})(\d{4})$/,"\\1#{delimiter}\\2#{delimiter}\\3")
-
number.slice!(0, 1) if number.start_with?(delimiter) && !delimiter.blank?
-
end
-
-
str = ''
-
str << "+#{country_code}#{delimiter}" unless country_code.blank?
-
str << number
-
str << " x #{extension}" unless extension.blank?
-
str
-
end
-
-
# Formats a +number+ into a currency string (e.g., $13.65). You
-
# can customize the format in the +options+ hash.
-
#
-
# ==== Options
-
#
-
# * <tt>:locale</tt> - Sets the locale to be used for formatting
-
# (defaults to current locale).
-
# * <tt>:precision</tt> - Sets the level of precision (defaults
-
# to 2).
-
# * <tt>:unit</tt> - Sets the denomination of the currency
-
# (defaults to "$").
-
# * <tt>:separator</tt> - Sets the separator between the units
-
# (defaults to ".").
-
# * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults
-
# to ",").
-
# * <tt>:format</tt> - Sets the format for non-negative numbers
-
# (defaults to "%u%n"). Fields are <tt>%u</tt> for the
-
# currency, and <tt>%n</tt> for the number.
-
# * <tt>:negative_format</tt> - Sets the format for negative
-
# numbers (defaults to prepending an hyphen to the formatted
-
# number given by <tt>:format</tt>). Accepts the same fields
-
# than <tt>:format</tt>, except <tt>%n</tt> is here the
-
# absolute value of the number.
-
#
-
# ==== Examples
-
#
-
# number_to_currency(1234567890.50) # => $1,234,567,890.50
-
# number_to_currency(1234567890.506) # => $1,234,567,890.51
-
# number_to_currency(1234567890.506, precision: 3) # => $1,234,567,890.506
-
# number_to_currency(1234567890.506, locale: :fr) # => 1 234 567 890,51 €
-
# number_to_currency('123a456') # => $123a456
-
#
-
# number_to_currency(-1234567890.50, negative_format: '(%u%n)')
-
# # => ($1,234,567,890.50)
-
# number_to_currency(1234567890.50, unit: '£', separator: ',', delimiter: '')
-
# # => £1234567890,50
-
# number_to_currency(1234567890.50, unit: '£', separator: ',', delimiter: '', format: '%n %u')
-
# # => 1234567890,50 £
-
1
def number_to_currency(number, options = {})
-
return unless number
-
options = options.symbolize_keys
-
-
currency = i18n_format_options(options[:locale], :currency)
-
currency[:negative_format] ||= "-" + currency[:format] if currency[:format]
-
-
defaults = default_format_options(:currency).merge!(currency)
-
defaults[:negative_format] = "-" + options[:format] if options[:format]
-
options = defaults.merge!(options)
-
-
unit = options.delete(:unit)
-
format = options.delete(:format)
-
-
if number.to_f.phase != 0
-
format = options.delete(:negative_format)
-
number = number.respond_to?("abs") ? number.abs : number.sub(/^-/, '')
-
end
-
-
format.gsub('%n', self.number_to_rounded(number, options)).gsub('%u', unit)
-
end
-
-
# Formats a +number+ as a percentage string (e.g., 65%). You can
-
# customize the format in the +options+ hash.
-
#
-
# ==== Options
-
#
-
# * <tt>:locale</tt> - Sets the locale to be used for formatting
-
# (defaults to current locale).
-
# * <tt>:precision</tt> - Sets the precision of the number
-
# (defaults to 3).
-
# * <tt>:significant</tt> - If +true+, precision will be the #
-
# of significant_digits. If +false+, the # of fractional
-
# digits (defaults to +false+).
-
# * <tt>:separator</tt> - Sets the separator between the
-
# fractional and integer digits (defaults to ".").
-
# * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults
-
# to "").
-
# * <tt>:strip_insignificant_zeros</tt> - If +true+ removes
-
# insignificant zeros after the decimal separator (defaults to
-
# +false+).
-
# * <tt>:format</tt> - Specifies the format of the percentage
-
# string The number field is <tt>%n</tt> (defaults to "%n%").
-
#
-
# ==== Examples
-
#
-
# number_to_percentage(100) # => 100.000%
-
# number_to_percentage('98') # => 98.000%
-
# number_to_percentage(100, precision: 0) # => 100%
-
# number_to_percentage(1000, delimiter: '.', separator: ,') # => 1.000,000%
-
# number_to_percentage(302.24398923423, precision: 5) # => 302.24399%
-
# number_to_percentage(1000, locale: :fr) # => 1 000,000%
-
# number_to_percentage('98a') # => 98a%
-
# number_to_percentage(100, format: '%n %') # => 100 %
-
1
def number_to_percentage(number, options = {})
-
return unless number
-
options = options.symbolize_keys
-
-
defaults = format_options(options[:locale], :percentage)
-
options = defaults.merge!(options)
-
-
format = options[:format] || "%n%"
-
format.gsub('%n', self.number_to_rounded(number, options))
-
end
-
-
# Formats a +number+ with grouped thousands using +delimiter+
-
# (e.g., 12,324). You can customize the format in the +options+
-
# hash.
-
#
-
# ==== Options
-
#
-
# * <tt>:locale</tt> - Sets the locale to be used for formatting
-
# (defaults to current locale).
-
# * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults
-
# to ",").
-
# * <tt>:separator</tt> - Sets the separator between the
-
# fractional and integer digits (defaults to ".").
-
#
-
# ==== Examples
-
#
-
# number_to_delimited(12345678) # => 12,345,678
-
# number_to_delimited('123456') # => 123,456
-
# number_to_delimited(12345678.05) # => 12,345,678.05
-
# number_to_delimited(12345678, delimiter: '.') # => 12.345.678
-
# number_to_delimited(12345678, delimiter: ',') # => 12,345,678
-
# number_to_delimited(12345678.05, separator: ' ') # => 12,345,678 05
-
# number_to_delimited(12345678.05, locale: :fr) # => 12 345 678,05
-
# number_to_delimited('112a') # => 112a
-
# number_to_delimited(98765432.98, delimiter: ' ', separator: ',')
-
# # => 98 765 432,98
-
1
def number_to_delimited(number, options = {})
-
options = options.symbolize_keys
-
-
return number unless valid_float?(number)
-
-
options = format_options(options[:locale]).merge!(options)
-
-
parts = number.to_s.to_str.split('.')
-
parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{options[:delimiter]}")
-
parts.join(options[:separator])
-
end
-
-
# Formats a +number+ with the specified level of
-
# <tt>:precision</tt> (e.g., 112.32 has a precision of 2 if
-
# +:significant+ is +false+, and 5 if +:significant+ is +true+).
-
# You can customize the format in the +options+ hash.
-
#
-
# ==== Options
-
#
-
# * <tt>:locale</tt> - Sets the locale to be used for formatting
-
# (defaults to current locale).
-
# * <tt>:precision</tt> - Sets the precision of the number
-
# (defaults to 3).
-
# * <tt>:significant</tt> - If +true+, precision will be the #
-
# of significant_digits. If +false+, the # of fractional
-
# digits (defaults to +false+).
-
# * <tt>:separator</tt> - Sets the separator between the
-
# fractional and integer digits (defaults to ".").
-
# * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults
-
# to "").
-
# * <tt>:strip_insignificant_zeros</tt> - If +true+ removes
-
# insignificant zeros after the decimal separator (defaults to
-
# +false+).
-
#
-
# ==== Examples
-
#
-
# number_to_rounded(111.2345) # => 111.235
-
# number_to_rounded(111.2345, precision: 2) # => 111.23
-
# number_to_rounded(13, precision: 5) # => 13.00000
-
# number_to_rounded(389.32314, precision: 0) # => 389
-
# number_to_rounded(111.2345, significant: true) # => 111
-
# number_to_rounded(111.2345, precision: 1, significant: true) # => 100
-
# number_to_rounded(13, precision: 5, significant: true) # => 13.000
-
# number_to_rounded(111.234, locale: :fr) # => 111,234
-
#
-
# number_to_rounded(13, precision: 5, significant: true, strip_insignificant_zeros: true)
-
# # => 13
-
#
-
# number_to_rounded(389.32314, precision: 4, significant: true) # => 389.3
-
# number_to_rounded(1111.2345, precision: 2, separator: ',', delimiter: '.')
-
# # => 1.111,23
-
1
def number_to_rounded(number, options = {})
-
return number unless valid_float?(number)
-
number = Float(number)
-
options = options.symbolize_keys
-
-
defaults = format_options(options[:locale], :precision)
-
options = defaults.merge!(options)
-
-
precision = options.delete :precision
-
significant = options.delete :significant
-
strip_insignificant_zeros = options.delete :strip_insignificant_zeros
-
-
if significant && precision > 0
-
if number == 0
-
digits, rounded_number = 1, 0
-
else
-
digits = (Math.log10(number.abs) + 1).floor
-
rounded_number = (BigDecimal.new(number.to_s) / BigDecimal.new((10 ** (digits - precision)).to_f.to_s)).round.to_f * 10 ** (digits - precision)
-
digits = (Math.log10(rounded_number.abs) + 1).floor # After rounding, the number of digits may have changed
-
end
-
precision -= digits
-
precision = 0 if precision < 0 # don't let it be negative
-
else
-
rounded_number = BigDecimal.new(number.to_s).round(precision).to_f
-
rounded_number = rounded_number.abs if rounded_number.zero? # prevent showing negative zeros
-
end
-
formatted_number = self.number_to_delimited("%01.#{precision}f" % rounded_number, options)
-
if strip_insignificant_zeros
-
escaped_separator = Regexp.escape(options[:separator])
-
formatted_number.sub(/(#{escaped_separator})(\d*[1-9])?0+\z/, '\1\2').sub(/#{escaped_separator}\z/, '')
-
else
-
formatted_number
-
end
-
end
-
-
# Formats the bytes in +number+ into a more understandable
-
# representation (e.g., giving it 1500 yields 1.5 KB). This
-
# method is useful for reporting file sizes to users. You can
-
# customize the format in the +options+ hash.
-
#
-
# See <tt>number_to_human</tt> if you want to pretty-print a
-
# generic number.
-
#
-
# ==== Options
-
#
-
# * <tt>:locale</tt> - Sets the locale to be used for formatting
-
# (defaults to current locale).
-
# * <tt>:precision</tt> - Sets the precision of the number
-
# (defaults to 3).
-
# * <tt>:significant</tt> - If +true+, precision will be the #
-
# of significant_digits. If +false+, the # of fractional
-
# digits (defaults to +true+)
-
# * <tt>:separator</tt> - Sets the separator between the
-
# fractional and integer digits (defaults to ".").
-
# * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults
-
# to "").
-
# * <tt>:strip_insignificant_zeros</tt> - If +true+ removes
-
# insignificant zeros after the decimal separator (defaults to
-
# +true+)
-
# * <tt>:prefix</tt> - If +:si+ formats the number using the SI
-
# prefix (defaults to :binary)
-
#
-
# ==== Examples
-
#
-
# number_to_human_size(123) # => 123 Bytes
-
# number_to_human_size(1234) # => 1.21 KB
-
# number_to_human_size(12345) # => 12.1 KB
-
# number_to_human_size(1234567) # => 1.18 MB
-
# number_to_human_size(1234567890) # => 1.15 GB
-
# number_to_human_size(1234567890123) # => 1.12 TB
-
# number_to_human_size(1234567, precision: 2) # => 1.2 MB
-
# number_to_human_size(483989, precision: 2) # => 470 KB
-
# number_to_human_size(1234567, precision: 2, separator: ',') # => 1,2 MB
-
#
-
# Non-significant zeros after the fractional separator are stripped out by
-
# default (set <tt>:strip_insignificant_zeros</tt> to +false+ to change that):
-
#
-
# number_to_human_size(1234567890123, precision: 5) # => "1.1229 TB"
-
# number_to_human_size(524288000, precision: 5) # => "500 MB"
-
1
def number_to_human_size(number, options = {})
-
options = options.symbolize_keys
-
-
return number unless valid_float?(number)
-
number = Float(number)
-
-
defaults = format_options(options[:locale], :human)
-
options = defaults.merge!(options)
-
-
#for backwards compatibility with those that didn't add strip_insignificant_zeros to their locale files
-
options[:strip_insignificant_zeros] = true if not options.key?(:strip_insignificant_zeros)
-
-
storage_units_format = translate_number_value_with_default('human.storage_units.format', :locale => options[:locale], :raise => true)
-
-
base = options[:prefix] == :si ? 1000 : 1024
-
-
if number.to_i < base
-
unit = translate_number_value_with_default('human.storage_units.units.byte', :locale => options[:locale], :count => number.to_i, :raise => true)
-
storage_units_format.gsub(/%n/, number.to_i.to_s).gsub(/%u/, unit)
-
else
-
max_exp = STORAGE_UNITS.size - 1
-
exponent = (Math.log(number) / Math.log(base)).to_i # Convert to base
-
exponent = max_exp if exponent > max_exp # we need this to avoid overflow for the highest unit
-
number /= base ** exponent
-
-
unit_key = STORAGE_UNITS[exponent]
-
unit = translate_number_value_with_default("human.storage_units.units.#{unit_key}", :locale => options[:locale], :count => number, :raise => true)
-
-
formatted_number = self.number_to_rounded(number, options)
-
storage_units_format.gsub(/%n/, formatted_number).gsub(/%u/, unit)
-
end
-
end
-
-
# Pretty prints (formats and approximates) a number in a way it
-
# is more readable by humans (eg.: 1200000000 becomes "1.2
-
# Billion"). This is useful for numbers that can get very large
-
# (and too hard to read).
-
#
-
# See <tt>number_to_human_size</tt> if you want to print a file
-
# size.
-
#
-
# You can also define you own unit-quantifier names if you want
-
# to use other decimal units (eg.: 1500 becomes "1.5
-
# kilometers", 0.150 becomes "150 milliliters", etc). You may
-
# define a wide range of unit quantifiers, even fractional ones
-
# (centi, deci, mili, etc).
-
#
-
# ==== Options
-
#
-
# * <tt>:locale</tt> - Sets the locale to be used for formatting
-
# (defaults to current locale).
-
# * <tt>:precision</tt> - Sets the precision of the number
-
# (defaults to 3).
-
# * <tt>:significant</tt> - If +true+, precision will be the #
-
# of significant_digits. If +false+, the # of fractional
-
# digits (defaults to +true+)
-
# * <tt>:separator</tt> - Sets the separator between the
-
# fractional and integer digits (defaults to ".").
-
# * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults
-
# to "").
-
# * <tt>:strip_insignificant_zeros</tt> - If +true+ removes
-
# insignificant zeros after the decimal separator (defaults to
-
# +true+)
-
# * <tt>:units</tt> - A Hash of unit quantifier names. Or a
-
# string containing an i18n scope where to find this hash. It
-
# might have the following keys:
-
# * *integers*: <tt>:unit</tt>, <tt>:ten</tt>,
-
# *<tt>:hundred</tt>, <tt>:thousand</tt>, <tt>:million</tt>,
-
# *<tt>:billion</tt>, <tt>:trillion</tt>,
-
# *<tt>:quadrillion</tt>
-
# * *fractionals*: <tt>:deci</tt>, <tt>:centi</tt>,
-
# *<tt>:mili</tt>, <tt>:micro</tt>, <tt>:nano</tt>,
-
# *<tt>:pico</tt>, <tt>:femto</tt>
-
# * <tt>:format</tt> - Sets the format of the output string
-
# (defaults to "%n %u"). The field types are:
-
# * %u - The quantifier (ex.: 'thousand')
-
# * %n - The number
-
#
-
# ==== Examples
-
#
-
# number_to_human(123) # => "123"
-
# number_to_human(1234) # => "1.23 Thousand"
-
# number_to_human(12345) # => "12.3 Thousand"
-
# number_to_human(1234567) # => "1.23 Million"
-
# number_to_human(1234567890) # => "1.23 Billion"
-
# number_to_human(1234567890123) # => "1.23 Trillion"
-
# number_to_human(1234567890123456) # => "1.23 Quadrillion"
-
# number_to_human(1234567890123456789) # => "1230 Quadrillion"
-
# number_to_human(489939, precision: 2) # => "490 Thousand"
-
# number_to_human(489939, precision: 4) # => "489.9 Thousand"
-
# number_to_human(1234567, precision: 4,
-
# significant: false) # => "1.2346 Million"
-
# number_to_human(1234567, precision: 1,
-
# separator: ',',
-
# significant: false) # => "1,2 Million"
-
#
-
# Non-significant zeros after the decimal separator are stripped
-
# out by default (set <tt>:strip_insignificant_zeros</tt> to
-
# +false+ to change that):
-
#
-
# number_to_human(12345012345, significant_digits: 6) # => "12.345 Billion"
-
# number_to_human(500000000, precision: 5) # => "500 Million"
-
#
-
# ==== Custom Unit Quantifiers
-
#
-
# You can also use your own custom unit quantifiers:
-
# number_to_human(500000, units: { unit: 'ml', thousand: 'lt' }) # => "500 lt"
-
#
-
# If in your I18n locale you have:
-
#
-
# distance:
-
# centi:
-
# one: "centimeter"
-
# other: "centimeters"
-
# unit:
-
# one: "meter"
-
# other: "meters"
-
# thousand:
-
# one: "kilometer"
-
# other: "kilometers"
-
# billion: "gazillion-distance"
-
#
-
# Then you could do:
-
#
-
# number_to_human(543934, units: :distance) # => "544 kilometers"
-
# number_to_human(54393498, units: :distance) # => "54400 kilometers"
-
# number_to_human(54393498000, units: :distance) # => "54.4 gazillion-distance"
-
# number_to_human(343, units: :distance, precision: 1) # => "300 meters"
-
# number_to_human(1, units: :distance) # => "1 meter"
-
# number_to_human(0.34, units: :distance) # => "34 centimeters"
-
1
def number_to_human(number, options = {})
-
options = options.symbolize_keys
-
-
return number unless valid_float?(number)
-
number = Float(number)
-
-
defaults = format_options(options[:locale], :human)
-
options = defaults.merge!(options)
-
-
#for backwards compatibility with those that didn't add strip_insignificant_zeros to their locale files
-
options[:strip_insignificant_zeros] = true if not options.key?(:strip_insignificant_zeros)
-
-
inverted_du = DECIMAL_UNITS.invert
-
-
units = options.delete :units
-
unit_exponents = case units
-
when Hash
-
units
-
when String, Symbol
-
I18n.translate(:"#{units}", :locale => options[:locale], :raise => true)
-
when nil
-
translate_number_value_with_default("human.decimal_units.units", :locale => options[:locale], :raise => true)
-
else
-
raise ArgumentError, ":units must be a Hash or String translation scope."
-
end.keys.map{|e_name| inverted_du[e_name] }.sort_by{|e| -e}
-
-
number_exponent = number != 0 ? Math.log10(number.abs).floor : 0
-
display_exponent = unit_exponents.find{ |e| number_exponent >= e } || 0
-
number /= 10 ** display_exponent
-
-
unit = case units
-
when Hash
-
units[DECIMAL_UNITS[display_exponent]]
-
when String, Symbol
-
I18n.translate(:"#{units}.#{DECIMAL_UNITS[display_exponent]}", :locale => options[:locale], :count => number.to_i)
-
else
-
translate_number_value_with_default("human.decimal_units.units.#{DECIMAL_UNITS[display_exponent]}", :locale => options[:locale], :count => number.to_i)
-
end
-
-
decimal_format = options[:format] || translate_number_value_with_default('human.decimal_units.format', :locale => options[:locale])
-
formatted_number = self.number_to_rounded(number, options)
-
decimal_format.gsub(/%n/, formatted_number).gsub(/%u/, unit).strip
-
end
-
-
1
def self.private_module_and_instance_method(method_name) #:nodoc:
-
5
private method_name
-
5
private_class_method method_name
-
end
-
1
private_class_method :private_module_and_instance_method
-
-
1
def format_options(locale, namespace = nil) #:nodoc:
-
default_format_options(namespace).merge!(i18n_format_options(locale, namespace))
-
end
-
1
private_module_and_instance_method :format_options
-
-
1
def default_format_options(namespace = nil) #:nodoc:
-
options = DEFAULTS[:format].dup
-
options.merge!(DEFAULTS[namespace][:format]) if namespace
-
options
-
end
-
1
private_module_and_instance_method :default_format_options
-
-
1
def i18n_format_options(locale, namespace = nil) #:nodoc:
-
options = I18n.translate(:'number.format', locale: locale, default: {}).dup
-
if namespace
-
options.merge!(I18n.translate(:"number.#{namespace}.format", locale: locale, default: {}))
-
end
-
options
-
end
-
1
private_module_and_instance_method :i18n_format_options
-
-
1
def translate_number_value_with_default(key, i18n_options = {}) #:nodoc:
-
default = key.split('.').reduce(DEFAULTS) { |defaults, k| defaults[k.to_sym] }
-
-
I18n.translate(key, { default: default, scope: :number }.merge!(i18n_options))
-
end
-
1
private_module_and_instance_method :translate_number_value_with_default
-
-
1
def valid_float?(number) #:nodoc:
-
Float(number)
-
rescue ArgumentError, TypeError
-
false
-
end
-
1
private_module_and_instance_method :valid_float?
-
end
-
end
-
1
require 'active_support/core_ext/hash/deep_merge'
-
-
1
module ActiveSupport
-
1
class OptionMerger #:nodoc:
-
1
instance_methods.each do |method|
-
123
undef_method(method) if method !~ /^(__|instance_eval|class|object_id)/
-
end
-
-
1
def initialize(context, options)
-
@context, @options = context, options
-
end
-
-
1
private
-
1
def method_missing(method, *arguments, &block)
-
if arguments.last.is_a?(Proc)
-
proc = arguments.pop
-
arguments << lambda { |*args| @options.deep_merge(proc.call(*args)) }
-
else
-
arguments << (arguments.last.respond_to?(:to_hash) ? @options.deep_merge(arguments.pop) : @options.dup)
-
end
-
-
@context.__send__(method, *arguments, &block)
-
end
-
end
-
end
-
1
module ActiveSupport
-
# Usually key value pairs are handled something like this:
-
#
-
# h = {}
-
# h[:boy] = 'John'
-
# h[:girl] = 'Mary'
-
# h[:boy] # => 'John'
-
# h[:girl] # => 'Mary'
-
#
-
# Using +OrderedOptions+, the above code could be reduced to:
-
#
-
# h = ActiveSupport::OrderedOptions.new
-
# h.boy = 'John'
-
# h.girl = 'Mary'
-
# h.boy # => 'John'
-
# h.girl # => 'Mary'
-
1
class OrderedOptions < Hash
-
1
alias_method :_get, :[] # preserve the original #[] method
-
1
protected :_get # make it protected
-
-
1
def []=(key, value)
-
162
super(key.to_sym, value)
-
end
-
-
1
def [](key)
-
107
super(key.to_sym)
-
end
-
-
1
def method_missing(name, *args)
-
269
name_string = name.to_s
-
269
if name_string.chomp!('=')
-
162
self[name_string] = args.first
-
else
-
107
self[name]
-
end
-
end
-
-
1
def respond_to_missing?(name, include_private)
-
14
true
-
end
-
end
-
-
1
class InheritableOptions < OrderedOptions
-
1
def initialize(parent = nil)
-
410
if parent.kind_of?(OrderedOptions)
-
# use the faster _get when dealing with OrderedOptions
-
335
super() { |h,k| parent._get(k) }
-
138
elsif parent
-
super() { |h,k| parent[k] }
-
else
-
138
super()
-
end
-
end
-
-
1
def inheritable_copy
-
272
self.class.new(self)
-
end
-
end
-
end
-
# This is private interface.
-
#
-
# Rails components cherry pick from Active Support as needed, but there are a
-
# few features that are used for sure some way or another and it is not worth
-
# to put individual requires absolutely everywhere. Think blank? for example.
-
#
-
# This file is loaded by every Rails component except Active Support itself,
-
# but it does not belong to the Rails public interface. It is internal to
-
# Rails and can change anytime.
-
-
# Defines Object#blank? and Object#present?.
-
1
require 'active_support/core_ext/object/blank'
-
-
# Rails own autoload, eager_load, etc.
-
1
require 'active_support/dependencies/autoload'
-
-
# Support for ClassMethods and the included macro.
-
1
require 'active_support/concern'
-
-
# Defines Class#class_attribute.
-
1
require 'active_support/core_ext/class/attribute'
-
-
# Defines Module#delegate.
-
1
require 'active_support/core_ext/module/delegation'
-
-
# Defines ActiveSupport::Deprecation.
-
1
require 'active_support/deprecation'
-
1
require 'active_support/concern'
-
1
require 'active_support/core_ext/class/attribute'
-
1
require 'active_support/core_ext/proc'
-
1
require 'active_support/core_ext/string/inflections'
-
1
require 'active_support/core_ext/array/extract_options'
-
-
1
module ActiveSupport
-
# Rescuable module adds support for easier exception handling.
-
1
module Rescuable
-
1
extend Concern
-
-
1
included do
-
1
class_attribute :rescue_handlers
-
1
self.rescue_handlers = []
-
end
-
-
1
module ClassMethods
-
# Rescue exceptions raised in controller actions.
-
#
-
# <tt>rescue_from</tt> receives a series of exception classes or class
-
# names, and a trailing <tt>:with</tt> option with the name of a method
-
# or a Proc object to be called to handle them. Alternatively a block can
-
# be given.
-
#
-
# Handlers that take one argument will be called with the exception, so
-
# that the exception can be inspected when dealing with it.
-
#
-
# Handlers are inherited. They are searched from right to left, from
-
# bottom to top, and up the hierarchy. The handler of the first class for
-
# which <tt>exception.is_a?(klass)</tt> holds true is the one invoked, if
-
# any.
-
#
-
# class ApplicationController < ActionController::Base
-
# rescue_from User::NotAuthorized, with: :deny_access # self defined exception
-
# rescue_from ActiveRecord::RecordInvalid, with: :show_errors
-
#
-
# rescue_from 'MyAppError::Base' do |exception|
-
# render xml: exception, status: 500
-
# end
-
#
-
# protected
-
# def deny_access
-
# ...
-
# end
-
#
-
# def show_errors(exception)
-
# exception.record.new_record? ? ...
-
# end
-
# end
-
#
-
# Exceptions raised inside exception handlers are not propagated up.
-
1
def rescue_from(*klasses, &block)
-
1
options = klasses.extract_options!
-
-
1
unless options.has_key?(:with)
-
1
if block_given?
-
1
options[:with] = block
-
else
-
raise ArgumentError, "Need a handler. Supply an options hash that has a :with key as the last argument."
-
end
-
end
-
-
1
klasses.each do |klass|
-
1
key = if klass.is_a?(Class) && klass <= Exception
-
1
klass.name
-
elsif klass.is_a?(String)
-
klass
-
else
-
raise ArgumentError, "#{klass} is neither an Exception nor a String"
-
end
-
-
# put the new handler at the end because the list is read in reverse
-
1
self.rescue_handlers += [[key, options[:with]]]
-
end
-
end
-
end
-
-
# Tries to rescue the exception by looking up and calling a registered handler.
-
1
def rescue_with_handler(exception)
-
if handler = handler_for_rescue(exception)
-
handler.arity != 0 ? handler.call(exception) : handler.call
-
true # don't rely on the return value of the handler
-
end
-
end
-
-
1
def handler_for_rescue(exception)
-
# We go from right to left because pairs are pushed onto rescue_handlers
-
# as rescue_from declarations are found.
-
_, rescuer = self.class.rescue_handlers.reverse.detect do |klass_name, handler|
-
# The purpose of allowing strings in rescue_from is to support the
-
# declaration of handler associations for exception classes whose
-
# definition is yet unknown.
-
#
-
# Since this loop needs the constants it would be inconsistent to
-
# assume they should exist at this point. An early raised exception
-
# could trigger some other handler and the array could include
-
# precisely a string whose corresponding constant has not yet been
-
# seen. This is why we are tolerant to unknown constants.
-
#
-
# Note that this tolerance only matters if the exception was given as
-
# a string, otherwise a NameError will be raised by the interpreter
-
# itself when rescue_from CONSTANT is executed.
-
klass = self.class.const_get(klass_name) rescue nil
-
klass ||= klass_name.constantize rescue nil
-
exception.is_a?(klass) if klass
-
end
-
-
case rescuer
-
when Symbol
-
method(rescuer)
-
when Proc
-
if rescuer.arity == 0
-
Proc.new { instance_exec(&rescuer) }
-
else
-
Proc.new { |_exception| instance_exec(_exception, &rescuer) }
-
end
-
end
-
end
-
end
-
end
-
1
gem 'minitest' # make sure we get the gem, not stdlib
-
1
require 'minitest/spec'
-
1
require 'active_support/testing/tagged_logging'
-
1
require 'active_support/testing/setup_and_teardown'
-
1
require 'active_support/testing/assertions'
-
1
require 'active_support/testing/deprecation'
-
1
require 'active_support/testing/isolation'
-
1
require 'active_support/testing/mocha_module'
-
1
require 'active_support/testing/constant_lookup'
-
1
require 'active_support/core_ext/kernel/reporting'
-
1
require 'active_support/deprecation'
-
-
1
module ActiveSupport
-
1
class TestCase < ::MiniTest::Spec
-
-
1
include ActiveSupport::Testing::MochaModule
-
-
# Use AS::TestCase for the base class when describing a model
-
1
register_spec_type(self) do |desc|
-
5
Class === desc && desc < ActiveRecord::Base
-
end
-
-
1
Assertion = MiniTest::Assertion
-
1
alias_method :method_name, :__name__
-
-
1
$tags = {}
-
1
def self.for_tag(tag)
-
yield if $tags[tag]
-
end
-
-
# FIXME: we have tests that depend on run order, we should fix that and
-
# remove this method.
-
1
def self.test_order # :nodoc:
-
92
:sorted
-
end
-
-
1
include ActiveSupport::Testing::TaggedLogging
-
1
include ActiveSupport::Testing::SetupAndTeardown
-
1
include ActiveSupport::Testing::Assertions
-
1
include ActiveSupport::Testing::Deprecation
-
-
1
def self.describe(text)
-
8
if block_given?
-
8
super
-
else
-
message = "`describe` without a block is deprecated, please switch to: `def self.name; #{text.inspect}; end`\n"
-
ActiveSupport::Deprecation.warn message
-
-
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
-
def self.name
-
"#{text}"
-
end
-
RUBY_EVAL
-
end
-
end
-
-
1
class << self
-
1
alias :test :it
-
end
-
-
# test/unit backwards compatibility methods
-
1
alias :assert_raise :assert_raises
-
1
alias :assert_not_nil :refute_nil
-
1
alias :assert_not_equal :refute_equal
-
1
alias :assert_no_match :refute_match
-
1
alias :assert_not_same :refute_same
-
-
# Fails if the block raises an exception.
-
#
-
# assert_nothing_raised do
-
# ...
-
# end
-
1
def assert_nothing_raised(*args)
-
11
yield
-
end
-
end
-
end
-
1
require 'active_support/core_ext/object/blank'
-
-
1
module ActiveSupport
-
1
module Testing
-
1
module Assertions
-
# Test numeric difference between the return value of an expression as a
-
# result of what is evaluated in the yielded block.
-
#
-
# assert_difference 'Article.count' do
-
# post :create, article: {...}
-
# end
-
#
-
# An arbitrary expression is passed in and evaluated.
-
#
-
# assert_difference 'assigns(:article).comments(:reload).size' do
-
# post :create, comment: {...}
-
# end
-
#
-
# An arbitrary positive or negative difference can be specified.
-
# The default is <tt>1</tt>.
-
#
-
# assert_difference 'Article.count', -1 do
-
# post :delete, id: ...
-
# end
-
#
-
# An array of expressions can also be passed in and evaluated.
-
#
-
# assert_difference [ 'Article.count', 'Post.count' ], 2 do
-
# post :create, article: {...}
-
# end
-
#
-
# A lambda or a list of lambdas can be passed in and evaluated:
-
#
-
# assert_difference ->{ Article.count }, 2 do
-
# post :create, article: {...}
-
# end
-
#
-
# assert_difference [->{ Article.count }, ->{ Post.count }], 2 do
-
# post :create, article: {...}
-
# end
-
#
-
# An error message can be specified.
-
#
-
# assert_difference 'Article.count', -1, 'An Article should be destroyed' do
-
# post :delete, id: ...
-
# end
-
1
def assert_difference(expression, difference = 1, message = nil, &block)
-
expressions = Array(expression)
-
-
exps = expressions.map { |e|
-
e.respond_to?(:call) ? e : lambda { eval(e, block.binding) }
-
}
-
before = exps.map { |e| e.call }
-
-
yield
-
-
expressions.zip(exps).each_with_index do |(code, e), i|
-
error = "#{code.inspect} didn't change by #{difference}"
-
error = "#{message}.\n#{error}" if message
-
assert_equal(before[i] + difference, e.call, error)
-
end
-
end
-
-
# Assertion that the numeric result of evaluating an expression is not
-
# changed before and after invoking the passed in block.
-
#
-
# assert_no_difference 'Article.count' do
-
# post :create, article: invalid_attributes
-
# end
-
#
-
# An error message can be specified.
-
#
-
# assert_no_difference 'Article.count', 'An Article should not be created' do
-
# post :create, article: invalid_attributes
-
# end
-
1
def assert_no_difference(expression, message = nil, &block)
-
assert_difference expression, 0, message, &block
-
end
-
-
# Test if an expression is blank. Passes if <tt>object.blank?</tt>
-
# is +true+.
-
#
-
# assert_blank [] # => true
-
# assert_blank [[]] # => [[]] is not blank
-
#
-
# An error message can be specified.
-
#
-
# assert_blank [], 'this should be blank'
-
1
def assert_blank(object, message=nil)
-
message ||= "#{object.inspect} is not blank"
-
assert object.blank?, message
-
end
-
-
# Test if an expression is not blank. Passes if <tt>object.present?</tt>
-
# is +true+.
-
#
-
# assert_present({ data: 'x' }) # => true
-
# assert_present({}) # => {} is blank
-
#
-
# An error message can be specified.
-
#
-
# assert_present({ data: 'x' }, 'this should not be blank')
-
1
def assert_present(object, message=nil)
-
message ||= "#{object.inspect} is blank"
-
assert object.present?, message
-
end
-
end
-
end
-
end
-
1
require "active_support/concern"
-
1
require "active_support/inflector"
-
-
1
module ActiveSupport
-
1
module Testing
-
# Resolves a constant from a minitest spec name.
-
#
-
# Given the following spec-style test:
-
#
-
# describe WidgetsController, :index do
-
# describe "authenticated user" do
-
# describe "returns widgets" do
-
# it "has a controller that exists" do
-
# assert_kind_of WidgetsController, @controller
-
# end
-
# end
-
# end
-
# end
-
#
-
# The test will have the following name:
-
#
-
# "WidgetsController::index::authenticated user::returns widgets"
-
#
-
# The constant WidgetsController can be resolved from the name.
-
# The following code will resolve the constant:
-
#
-
# controller = determine_constant_from_test_name(name) do |constant|
-
# Class === constant && constant < ::ActionController::Metal
-
# end
-
1
module ConstantLookup
-
1
extend ::ActiveSupport::Concern
-
-
1
module ClassMethods
-
1
def determine_constant_from_test_name(test_name)
-
10
names = test_name.split "::"
-
10
while names.size > 0 do
-
16
names.last.sub!(/Test$/, "")
-
16
begin
-
16
constant = names.join("::").constantize
-
9
break(constant) if yield(constant)
-
rescue NameError
-
# Constant wasn't found, move on
-
ensure
-
16
names.pop
-
end
-
end
-
end
-
end
-
-
end
-
end
-
end
-
1
require 'active_support/deprecation'
-
-
1
module ActiveSupport
-
1
module Testing
-
1
module Deprecation #:nodoc:
-
1
def assert_deprecated(match = nil, &block)
-
result, warnings = collect_deprecations(&block)
-
assert !warnings.empty?, "Expected a deprecation warning within the block but received none"
-
if match
-
match = Regexp.new(Regexp.escape(match)) unless match.is_a?(Regexp)
-
assert warnings.any? { |w| w =~ match }, "No deprecation warning matched #{match}: #{warnings.join(', ')}"
-
end
-
result
-
end
-
-
1
def assert_not_deprecated(&block)
-
result, deprecations = collect_deprecations(&block)
-
assert deprecations.empty?, "Expected no deprecation warning within the block but received #{deprecations.size}: \n #{deprecations * "\n "}"
-
result
-
end
-
-
1
private
-
1
def collect_deprecations
-
old_behavior = ActiveSupport::Deprecation.behavior
-
deprecations = []
-
ActiveSupport::Deprecation.behavior = Proc.new do |message, callstack|
-
deprecations << message
-
end
-
result = yield
-
[result, deprecations]
-
ensure
-
ActiveSupport::Deprecation.behavior = old_behavior
-
end
-
end
-
end
-
end
-
1
require 'rbconfig'
-
1
module ActiveSupport
-
1
module Testing
-
1
class RemoteError < StandardError
-
-
1
attr_reader :message, :backtrace
-
-
1
def initialize(exception)
-
@message = "caught #{exception.class.name}: #{exception.message}"
-
@backtrace = exception.backtrace
-
end
-
end
-
-
1
class ProxyTestResult
-
1
def initialize
-
@calls = []
-
end
-
-
1
def add_error(e)
-
e = Test::Unit::Error.new(e.test_name, RemoteError.new(e.exception))
-
@calls << [:add_error, e]
-
end
-
-
1
def __replay__(result)
-
@calls.each do |name, args|
-
result.send(name, *args)
-
end
-
end
-
-
1
def method_missing(name, *args)
-
@calls << [name, args]
-
end
-
end
-
-
1
module Isolation
-
1
require 'thread'
-
-
1
class ParallelEach
-
1
include Enumerable
-
-
# default to 2 cores
-
1
CORES = (ENV['TEST_CORES'] || 2).to_i
-
-
1
def initialize list
-
@list = list
-
@queue = SizedQueue.new CORES
-
end
-
-
1
def grep pattern
-
self.class.new super
-
end
-
-
1
def each
-
threads = CORES.times.map {
-
Thread.new {
-
while job = @queue.pop
-
yield job
-
end
-
}
-
}
-
@list.each { |i| @queue << i }
-
CORES.times { @queue << nil }
-
threads.each(&:join)
-
end
-
end
-
-
1
def self.included(klass) #:nodoc:
-
klass.extend(Module.new {
-
def test_methods
-
ParallelEach.new super
-
end
-
})
-
end
-
-
1
def self.forking_env?
-
1
!ENV["NO_FORK"] && ((RbConfig::CONFIG['host_os'] !~ /mswin|mingw/) && (RUBY_PLATFORM !~ /java/))
-
end
-
-
1
def _run_class_setup # class setup method should only happen in parent
-
unless defined?(@@ran_class_setup) || ENV['ISOLATION_TEST']
-
self.class.setup if self.class.respond_to?(:setup)
-
@@ran_class_setup = true
-
end
-
end
-
-
1
def run(runner)
-
_run_class_setup
-
-
serialized = run_in_isolation do |isolated_runner|
-
super(isolated_runner)
-
end
-
-
retval, proxy = Marshal.load(serialized)
-
proxy.__replay__(runner)
-
retval
-
end
-
-
1
module Forking
-
1
def run_in_isolation(&blk)
-
read, write = IO.pipe
-
-
pid = fork do
-
read.close
-
proxy = ProxyTestResult.new
-
retval = yield proxy
-
write.puts [Marshal.dump([retval, proxy])].pack("m")
-
exit!
-
end
-
-
write.close
-
result = read.read
-
Process.wait2(pid)
-
return result.unpack("m")[0]
-
end
-
end
-
-
1
module Subprocess
-
1
ORIG_ARGV = ARGV.dup unless defined?(ORIG_ARGV)
-
-
# Crazy H4X to get this working in windows / jruby with
-
# no forking.
-
1
def run_in_isolation(&blk)
-
require "tempfile"
-
-
if ENV["ISOLATION_TEST"]
-
proxy = ProxyTestResult.new
-
retval = yield proxy
-
File.open(ENV["ISOLATION_OUTPUT"], "w") do |file|
-
file.puts [Marshal.dump([retval, proxy])].pack("m")
-
end
-
exit!
-
else
-
Tempfile.open("isolation") do |tmpfile|
-
ENV["ISOLATION_TEST"] = @method_name
-
ENV["ISOLATION_OUTPUT"] = tmpfile.path
-
-
load_paths = $-I.map {|p| "-I\"#{File.expand_path(p)}\"" }.join(" ")
-
`#{Gem.ruby} #{load_paths} #{$0} #{ORIG_ARGV.join(" ")} -t\"#{self.class}\"`
-
-
ENV.delete("ISOLATION_TEST")
-
ENV.delete("ISOLATION_OUTPUT")
-
-
return tmpfile.read.unpack("m")[0]
-
end
-
end
-
end
-
end
-
-
1
include forking_env? ? Forking : Subprocess
-
end
-
end
-
end
-
1
module ActiveSupport
-
1
module Testing
-
1
module MochaModule
-
1
begin
-
1
require 'mocha/api'
-
1
include Mocha::API
-
-
1
def before_setup
-
146
mocha_setup
-
146
super
-
end
-
-
1
def after_teardown
-
146
super
-
146
mocha_verify
-
146
mocha_teardown
-
end
-
rescue LoadError
-
end
-
end
-
end
-
end
-
1
require 'active_support/concern'
-
1
require 'active_support/callbacks'
-
-
1
module ActiveSupport
-
1
module Testing
-
1
module SetupAndTeardown
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
1
include ActiveSupport::Callbacks
-
1
define_callbacks :setup, :teardown
-
end
-
-
1
module ClassMethods
-
1
def setup(*args, &block)
-
5
set_callback(:setup, :before, *args, &block)
-
end
-
-
1
def teardown(*args, &block)
-
2
set_callback(:teardown, :after, *args, &block)
-
end
-
end
-
-
1
def before_setup
-
146
super
-
146
run_callbacks :setup
-
end
-
-
1
def after_teardown
-
146
run_callbacks :teardown
-
146
super
-
end
-
end
-
end
-
end
-
1
module ActiveSupport
-
1
module Testing
-
1
module TaggedLogging
-
1
attr_writer :tagged_logger
-
-
1
def before_setup
-
146
tagged_logger.push_tags(self.class.name, __name__) if tagged_logging?
-
146
super
-
end
-
-
1
def after_teardown
-
146
super
-
146
tagged_logger.pop_tags(2) if tagged_logging?
-
end
-
-
1
private
-
1
def tagged_logger
-
292
@tagged_logger ||= (defined?(Rails.logger) && Rails.logger)
-
end
-
-
1
def tagged_logging?
-
292
tagged_logger && tagged_logger.respond_to?(:push_tags)
-
end
-
end
-
end
-
end
-
1
require 'active_support'
-
-
1
module ActiveSupport
-
1
autoload :Duration, 'active_support/duration'
-
1
autoload :TimeWithZone, 'active_support/time_with_zone'
-
1
autoload :TimeZone, 'active_support/values/time_zone'
-
end
-
-
1
require 'date'
-
1
require 'time'
-
-
1
require 'active_support/core_ext/time/marshal'
-
1
require 'active_support/core_ext/time/acts_like'
-
1
require 'active_support/core_ext/time/calculations'
-
1
require 'active_support/core_ext/time/conversions'
-
1
require 'active_support/core_ext/time/zones'
-
-
1
require 'active_support/core_ext/date/acts_like'
-
1
require 'active_support/core_ext/date/calculations'
-
1
require 'active_support/core_ext/date/conversions'
-
1
require 'active_support/core_ext/date/zones'
-
-
1
require 'active_support/core_ext/date_time/acts_like'
-
1
require 'active_support/core_ext/date_time/calculations'
-
1
require 'active_support/core_ext/date_time/conversions'
-
1
require 'active_support/core_ext/date_time/zones'
-
-
1
require 'active_support/core_ext/integer/time'
-
1
require 'active_support/core_ext/numeric/time'
-
1
require 'active_support/values/time_zone'
-
1
require 'active_support/core_ext/object/acts_like'
-
-
1
module ActiveSupport
-
# A Time-like class that can represent a time in any time zone. Necessary
-
# because standard Ruby Time instances are limited to UTC and the
-
# system's <tt>ENV['TZ']</tt> zone.
-
#
-
# You shouldn't ever need to create a TimeWithZone instance directly via +new+.
-
# Instead use methods +local+, +parse+, +at+ and +now+ on TimeZone instances,
-
# and +in_time_zone+ on Time and DateTime instances.
-
#
-
# Time.zone = 'Eastern Time (US & Canada)' # => 'Eastern Time (US & Canada)'
-
# Time.zone.local(2007, 2, 10, 15, 30, 45) # => Sat, 10 Feb 2007 15:30:45 EST -05:00
-
# Time.zone.parse('2007-02-10 15:30:45') # => Sat, 10 Feb 2007 15:30:45 EST -05:00
-
# Time.zone.at(1170361845) # => Sat, 10 Feb 2007 15:30:45 EST -05:00
-
# Time.zone.now # => Sun, 18 May 2008 13:07:55 EDT -04:00
-
# Time.utc(2007, 2, 10, 20, 30, 45).in_time_zone # => Sat, 10 Feb 2007 15:30:45 EST -05:00
-
#
-
# See Time and TimeZone for further documentation of these methods.
-
#
-
# TimeWithZone instances implement the same API as Ruby Time instances, so
-
# that Time and TimeWithZone instances are interchangeable.
-
#
-
# t = Time.zone.now # => Sun, 18 May 2008 13:27:25 EDT -04:00
-
# t.hour # => 13
-
# t.dst? # => true
-
# t.utc_offset # => -14400
-
# t.zone # => "EDT"
-
# t.to_s(:rfc822) # => "Sun, 18 May 2008 13:27:25 -0400"
-
# t + 1.day # => Mon, 19 May 2008 13:27:25 EDT -04:00
-
# t.beginning_of_year # => Tue, 01 Jan 2008 00:00:00 EST -05:00
-
# t > Time.utc(1999) # => true
-
# t.is_a?(Time) # => true
-
# t.is_a?(ActiveSupport::TimeWithZone) # => true
-
1
class TimeWithZone
-
-
# Report class name as 'Time' to thwart type checking.
-
1
def self.name
-
'Time'
-
end
-
-
1
include Comparable
-
1
attr_reader :time_zone
-
-
1
def initialize(utc_time, time_zone, local_time = nil, period = nil)
-
@utc, @time_zone, @time = utc_time, time_zone, local_time
-
@period = @utc ? period : get_period_and_ensure_valid_local_time
-
end
-
-
# Returns a Time or DateTime instance that represents the time in +time_zone+.
-
1
def time
-
@time ||= period.to_local(@utc)
-
end
-
-
# Returns a Time or DateTime instance that represents the time in UTC.
-
1
def utc
-
@utc ||= period.to_utc(@time)
-
end
-
1
alias_method :comparable_time, :utc
-
1
alias_method :getgm, :utc
-
1
alias_method :getutc, :utc
-
1
alias_method :gmtime, :utc
-
-
# Returns the underlying TZInfo::TimezonePeriod.
-
1
def period
-
@period ||= time_zone.period_for_utc(@utc)
-
end
-
-
# Returns the simultaneous time in <tt>Time.zone</tt>, or the specified zone.
-
1
def in_time_zone(new_zone = ::Time.zone)
-
return self if time_zone == new_zone
-
utc.in_time_zone(new_zone)
-
end
-
-
# Returns a <tt>Time.local()</tt> instance of the simultaneous time in your
-
# system's <tt>ENV['TZ']</tt> zone.
-
1
def localtime
-
utc.respond_to?(:getlocal) ? utc.getlocal : utc.to_time.getlocal
-
end
-
1
alias_method :getlocal, :localtime
-
-
1
def dst?
-
period.dst?
-
end
-
1
alias_method :isdst, :dst?
-
-
1
def utc?
-
time_zone.name == 'UTC'
-
end
-
1
alias_method :gmt?, :utc?
-
-
1
def utc_offset
-
period.utc_total_offset
-
end
-
1
alias_method :gmt_offset, :utc_offset
-
1
alias_method :gmtoff, :utc_offset
-
-
1
def formatted_offset(colon = true, alternate_utc_string = nil)
-
utc? && alternate_utc_string || TimeZone.seconds_to_utc_offset(utc_offset, colon)
-
end
-
-
# Time uses +zone+ to display the time zone abbreviation, so we're
-
# duck-typing it.
-
1
def zone
-
period.zone_identifier.to_s
-
end
-
-
1
def inspect
-
"#{time.strftime('%a, %d %b %Y %H:%M:%S')} #{zone} #{formatted_offset}"
-
end
-
-
1
def xmlschema(fraction_digits = 0)
-
fraction = if fraction_digits > 0
-
(".%06i" % time.usec)[0, fraction_digits + 1]
-
end
-
-
"#{time.strftime("%Y-%m-%dT%H:%M:%S")}#{fraction}#{formatted_offset(true, 'Z')}"
-
end
-
1
alias_method :iso8601, :xmlschema
-
-
# Coerces time to a string for JSON encoding. The default format is ISO 8601.
-
# You can get %Y/%m/%d %H:%M:%S +offset style by setting
-
# <tt>ActiveSupport::JSON::Encoding.use_standard_json_time_format</tt>
-
# to +false+.
-
#
-
# # With ActiveSupport::JSON::Encoding.use_standard_json_time_format = true
-
# Time.utc(2005,2,1,15,15,10).in_time_zone.to_json
-
# # => "2005-02-01T15:15:10Z"
-
#
-
# # With ActiveSupport::JSON::Encoding.use_standard_json_time_format = false
-
# Time.utc(2005,2,1,15,15,10).in_time_zone.to_json
-
# # => "2005/02/01 15:15:10 +0000"
-
1
def as_json(options = nil)
-
if ActiveSupport::JSON::Encoding.use_standard_json_time_format
-
xmlschema
-
else
-
%(#{time.strftime("%Y/%m/%d %H:%M:%S")} #{formatted_offset(false)})
-
end
-
end
-
-
1
def encode_with(coder)
-
if coder.respond_to?(:represent_object)
-
coder.represent_object(nil, utc)
-
else
-
coder.represent_scalar(nil, utc.strftime("%Y-%m-%d %H:%M:%S.%9NZ"))
-
end
-
end
-
-
1
def httpdate
-
utc.httpdate
-
end
-
-
1
def rfc2822
-
to_s(:rfc822)
-
end
-
1
alias_method :rfc822, :rfc2822
-
-
# <tt>:db</tt> format outputs time in UTC; all others output time in local.
-
# Uses TimeWithZone's +strftime+, so <tt>%Z</tt> and <tt>%z</tt> work correctly.
-
1
def to_s(format = :default)
-
if format == :db
-
utc.to_s(format)
-
elsif formatter = ::Time::DATE_FORMATS[format]
-
formatter.respond_to?(:call) ? formatter.call(self).to_s : strftime(formatter)
-
else
-
"#{time.strftime("%Y-%m-%d %H:%M:%S")} #{formatted_offset(false, 'UTC')}" # mimicking Ruby 1.9 Time#to_s format
-
end
-
end
-
1
alias_method :to_formatted_s, :to_s
-
-
# Replaces <tt>%Z</tt> and <tt>%z</tt> directives with +zone+ and
-
# +formatted_offset+, respectively, before passing to Time#strftime, so
-
# that zone information is correct
-
1
def strftime(format)
-
format = format.gsub('%Z', zone)
-
.gsub('%z', formatted_offset(false))
-
.gsub('%:z', formatted_offset(true))
-
.gsub('%::z', formatted_offset(true) + ":00")
-
time.strftime(format)
-
end
-
-
# Use the time in UTC for comparisons.
-
1
def <=>(other)
-
utc <=> other
-
end
-
-
1
def between?(min, max)
-
utc.between?(min, max)
-
end
-
-
1
def past?
-
utc.past?
-
end
-
-
1
def today?
-
time.today?
-
end
-
-
1
def future?
-
utc.future?
-
end
-
-
1
def eql?(other)
-
utc.eql?(other)
-
end
-
-
1
def hash
-
utc.hash
-
end
-
-
1
def +(other)
-
# If we're adding a Duration of variable length (i.e., years, months, days), move forward from #time,
-
# otherwise move forward from #utc, for accuracy when moving across DST boundaries
-
if duration_of_variable_length?(other)
-
method_missing(:+, other)
-
else
-
result = utc.acts_like?(:date) ? utc.since(other) : utc + other rescue utc.since(other)
-
result.in_time_zone(time_zone)
-
end
-
end
-
-
1
def -(other)
-
# If we're subtracting a Duration of variable length (i.e., years, months, days), move backwards from #time,
-
# otherwise move backwards #utc, for accuracy when moving across DST boundaries
-
if other.acts_like?(:time)
-
utc.to_f - other.to_f
-
elsif duration_of_variable_length?(other)
-
method_missing(:-, other)
-
else
-
result = utc.acts_like?(:date) ? utc.ago(other) : utc - other rescue utc.ago(other)
-
result.in_time_zone(time_zone)
-
end
-
end
-
-
1
def since(other)
-
# If we're adding a Duration of variable length (i.e., years, months, days), move forward from #time,
-
# otherwise move forward from #utc, for accuracy when moving across DST boundaries
-
if duration_of_variable_length?(other)
-
method_missing(:since, other)
-
else
-
utc.since(other).in_time_zone(time_zone)
-
end
-
end
-
-
1
def ago(other)
-
since(-other)
-
end
-
-
1
def advance(options)
-
# If we're advancing a value of variable length (i.e., years, weeks, months, days), advance from #time,
-
# otherwise advance from #utc, for accuracy when moving across DST boundaries
-
if options.values_at(:years, :weeks, :months, :days).any?
-
method_missing(:advance, options)
-
else
-
utc.advance(options).in_time_zone(time_zone)
-
end
-
end
-
-
1
%w(year mon month day mday wday yday hour min sec to_date).each do |method_name|
-
11
class_eval <<-EOV, __FILE__, __LINE__ + 1
-
def #{method_name} # def month
-
time.#{method_name} # time.month
-
end # end
-
EOV
-
end
-
-
1
def usec
-
time.respond_to?(:usec) ? time.usec : 0
-
end
-
-
1
def to_a
-
[time.sec, time.min, time.hour, time.day, time.mon, time.year, time.wday, time.yday, dst?, zone]
-
end
-
-
1
def to_f
-
utc.to_f
-
end
-
-
1
def to_i
-
utc.to_i
-
end
-
1
alias_method :tv_sec, :to_i
-
-
# A TimeWithZone acts like a Time, so just return +self+.
-
1
def to_time
-
utc
-
end
-
-
1
def to_datetime
-
utc.to_datetime.new_offset(Rational(utc_offset, 86_400))
-
end
-
-
# So that +self+ <tt>acts_like?(:time)</tt>.
-
1
def acts_like_time?
-
true
-
end
-
-
# Say we're a Time to thwart type checking.
-
1
def is_a?(klass)
-
klass == ::Time || super
-
end
-
1
alias_method :kind_of?, :is_a?
-
-
1
def freeze
-
period; utc; time # preload instance variables before freezing
-
super
-
end
-
-
1
def marshal_dump
-
[utc, time_zone.name, time]
-
end
-
-
1
def marshal_load(variables)
-
initialize(variables[0].utc, ::Time.find_zone(variables[1]), variables[2].utc)
-
end
-
-
# Ensure proxy class responds to all methods that underlying time instance
-
# responds to.
-
1
def respond_to_missing?(sym, include_priv)
-
# consistently respond false to acts_like?(:date), regardless of whether #time is a Time or DateTime
-
return false if sym.to_sym == :acts_like_date?
-
time.respond_to?(sym, include_priv)
-
end
-
-
# Send the missing method to +time+ instance, and wrap result in a new
-
# TimeWithZone with the existing +time_zone+.
-
1
def method_missing(sym, *args, &block)
-
wrap_with_time_zone time.__send__(sym, *args, &block)
-
end
-
-
1
private
-
1
def get_period_and_ensure_valid_local_time
-
# we don't want a Time.local instance enforcing its own DST rules as well,
-
# so transfer time values to a utc constructor if necessary
-
@time = transfer_time_values_to_utc_constructor(@time) unless @time.utc?
-
begin
-
@time_zone.period_for_local(@time)
-
rescue ::TZInfo::PeriodNotFound
-
# time is in the "spring forward" hour gap, so we're moving the time forward one hour and trying again
-
@time += 1.hour
-
retry
-
end
-
end
-
-
1
def transfer_time_values_to_utc_constructor(time)
-
::Time.utc_time(time.year, time.month, time.day, time.hour, time.min, time.sec, time.respond_to?(:nsec) ? Rational(time.nsec, 1000) : 0)
-
end
-
-
1
def duration_of_variable_length?(obj)
-
ActiveSupport::Duration === obj && obj.parts.any? {|p| [:years, :months, :days].include?(p[0]) }
-
end
-
-
1
def wrap_with_time_zone(time)
-
if time.acts_like?(:time)
-
self.class.new(nil, time_zone, time)
-
elsif time.is_a?(Range)
-
wrap_with_time_zone(time.begin)..wrap_with_time_zone(time.end)
-
else
-
time
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/object/blank'
-
1
require 'active_support/core_ext/object/try'
-
-
1
module ActiveSupport
-
# The TimeZone class serves as a wrapper around TZInfo::Timezone instances.
-
# It allows us to do the following:
-
#
-
# * Limit the set of zones provided by TZInfo to a meaningful subset of 142
-
# zones.
-
# * Retrieve and display zones with a friendlier name
-
# (e.g., "Eastern Time (US & Canada)" instead of "America/New_York").
-
# * Lazily load TZInfo::Timezone instances only when they're needed.
-
# * Create ActiveSupport::TimeWithZone instances via TimeZone's +local+,
-
# +parse+, +at+ and +now+ methods.
-
#
-
# If you set <tt>config.time_zone</tt> in the Rails Application, you can
-
# access this TimeZone object via <tt>Time.zone</tt>:
-
#
-
# # application.rb:
-
# class Application < Rails::Application
-
# config.time_zone = 'Eastern Time (US & Canada)'
-
# end
-
#
-
# Time.zone # => #<TimeZone:0x514834...>
-
# Time.zone.name # => "Eastern Time (US & Canada)"
-
# Time.zone.now # => Sun, 18 May 2008 14:30:44 EDT -04:00
-
#
-
# The version of TZInfo bundled with Active Support only includes the
-
# definitions necessary to support the zones defined by the TimeZone class.
-
# If you need to use zones that aren't defined by TimeZone, you'll need to
-
# install the TZInfo gem (if a recent version of the gem is installed locally,
-
# this will be used instead of the bundled version.)
-
1
class TimeZone
-
# Keys are Rails TimeZone names, values are TZInfo identifiers.
-
1
MAPPING = {
-
"International Date Line West" => "Pacific/Midway",
-
"Midway Island" => "Pacific/Midway",
-
"American Samoa" => "Pacific/Pago_Pago",
-
"Hawaii" => "Pacific/Honolulu",
-
"Alaska" => "America/Juneau",
-
"Pacific Time (US & Canada)" => "America/Los_Angeles",
-
"Tijuana" => "America/Tijuana",
-
"Mountain Time (US & Canada)" => "America/Denver",
-
"Arizona" => "America/Phoenix",
-
"Chihuahua" => "America/Chihuahua",
-
"Mazatlan" => "America/Mazatlan",
-
"Central Time (US & Canada)" => "America/Chicago",
-
"Saskatchewan" => "America/Regina",
-
"Guadalajara" => "America/Mexico_City",
-
"Mexico City" => "America/Mexico_City",
-
"Monterrey" => "America/Monterrey",
-
"Central America" => "America/Guatemala",
-
"Eastern Time (US & Canada)" => "America/New_York",
-
"Indiana (East)" => "America/Indiana/Indianapolis",
-
"Bogota" => "America/Bogota",
-
"Lima" => "America/Lima",
-
"Quito" => "America/Lima",
-
"Atlantic Time (Canada)" => "America/Halifax",
-
"Caracas" => "America/Caracas",
-
"La Paz" => "America/La_Paz",
-
"Santiago" => "America/Santiago",
-
"Newfoundland" => "America/St_Johns",
-
"Brasilia" => "America/Sao_Paulo",
-
"Buenos Aires" => "America/Argentina/Buenos_Aires",
-
"Georgetown" => "America/Guyana",
-
"Greenland" => "America/Godthab",
-
"Mid-Atlantic" => "Atlantic/South_Georgia",
-
"Azores" => "Atlantic/Azores",
-
"Cape Verde Is." => "Atlantic/Cape_Verde",
-
"Dublin" => "Europe/Dublin",
-
"Edinburgh" => "Europe/London",
-
"Lisbon" => "Europe/Lisbon",
-
"London" => "Europe/London",
-
"Casablanca" => "Africa/Casablanca",
-
"Monrovia" => "Africa/Monrovia",
-
"UTC" => "Etc/UTC",
-
"Belgrade" => "Europe/Belgrade",
-
"Bratislava" => "Europe/Bratislava",
-
"Budapest" => "Europe/Budapest",
-
"Ljubljana" => "Europe/Ljubljana",
-
"Prague" => "Europe/Prague",
-
"Sarajevo" => "Europe/Sarajevo",
-
"Skopje" => "Europe/Skopje",
-
"Warsaw" => "Europe/Warsaw",
-
"Zagreb" => "Europe/Zagreb",
-
"Brussels" => "Europe/Brussels",
-
"Copenhagen" => "Europe/Copenhagen",
-
"Madrid" => "Europe/Madrid",
-
"Paris" => "Europe/Paris",
-
"Amsterdam" => "Europe/Amsterdam",
-
"Berlin" => "Europe/Berlin",
-
"Bern" => "Europe/Berlin",
-
"Rome" => "Europe/Rome",
-
"Stockholm" => "Europe/Stockholm",
-
"Vienna" => "Europe/Vienna",
-
"West Central Africa" => "Africa/Algiers",
-
"Bucharest" => "Europe/Bucharest",
-
"Cairo" => "Africa/Cairo",
-
"Helsinki" => "Europe/Helsinki",
-
"Kyiv" => "Europe/Kiev",
-
"Riga" => "Europe/Riga",
-
"Sofia" => "Europe/Sofia",
-
"Tallinn" => "Europe/Tallinn",
-
"Vilnius" => "Europe/Vilnius",
-
"Athens" => "Europe/Athens",
-
"Istanbul" => "Europe/Istanbul",
-
"Minsk" => "Europe/Minsk",
-
"Jerusalem" => "Asia/Jerusalem",
-
"Harare" => "Africa/Harare",
-
"Pretoria" => "Africa/Johannesburg",
-
"Moscow" => "Europe/Moscow",
-
"St. Petersburg" => "Europe/Moscow",
-
"Volgograd" => "Europe/Moscow",
-
"Kuwait" => "Asia/Kuwait",
-
"Riyadh" => "Asia/Riyadh",
-
"Nairobi" => "Africa/Nairobi",
-
"Baghdad" => "Asia/Baghdad",
-
"Tehran" => "Asia/Tehran",
-
"Abu Dhabi" => "Asia/Muscat",
-
"Muscat" => "Asia/Muscat",
-
"Baku" => "Asia/Baku",
-
"Tbilisi" => "Asia/Tbilisi",
-
"Yerevan" => "Asia/Yerevan",
-
"Kabul" => "Asia/Kabul",
-
"Ekaterinburg" => "Asia/Yekaterinburg",
-
"Islamabad" => "Asia/Karachi",
-
"Karachi" => "Asia/Karachi",
-
"Tashkent" => "Asia/Tashkent",
-
"Chennai" => "Asia/Kolkata",
-
"Kolkata" => "Asia/Kolkata",
-
"Mumbai" => "Asia/Kolkata",
-
"New Delhi" => "Asia/Kolkata",
-
"Kathmandu" => "Asia/Kathmandu",
-
"Astana" => "Asia/Dhaka",
-
"Dhaka" => "Asia/Dhaka",
-
"Sri Jayawardenepura" => "Asia/Colombo",
-
"Almaty" => "Asia/Almaty",
-
"Novosibirsk" => "Asia/Novosibirsk",
-
"Rangoon" => "Asia/Rangoon",
-
"Bangkok" => "Asia/Bangkok",
-
"Hanoi" => "Asia/Bangkok",
-
"Jakarta" => "Asia/Jakarta",
-
"Krasnoyarsk" => "Asia/Krasnoyarsk",
-
"Beijing" => "Asia/Shanghai",
-
"Chongqing" => "Asia/Chongqing",
-
"Hong Kong" => "Asia/Hong_Kong",
-
"Urumqi" => "Asia/Urumqi",
-
"Kuala Lumpur" => "Asia/Kuala_Lumpur",
-
"Singapore" => "Asia/Singapore",
-
"Taipei" => "Asia/Taipei",
-
"Perth" => "Australia/Perth",
-
"Irkutsk" => "Asia/Irkutsk",
-
"Ulaan Bataar" => "Asia/Ulaanbaatar",
-
"Seoul" => "Asia/Seoul",
-
"Osaka" => "Asia/Tokyo",
-
"Sapporo" => "Asia/Tokyo",
-
"Tokyo" => "Asia/Tokyo",
-
"Yakutsk" => "Asia/Yakutsk",
-
"Darwin" => "Australia/Darwin",
-
"Adelaide" => "Australia/Adelaide",
-
"Canberra" => "Australia/Melbourne",
-
"Melbourne" => "Australia/Melbourne",
-
"Sydney" => "Australia/Sydney",
-
"Brisbane" => "Australia/Brisbane",
-
"Hobart" => "Australia/Hobart",
-
"Vladivostok" => "Asia/Vladivostok",
-
"Guam" => "Pacific/Guam",
-
"Port Moresby" => "Pacific/Port_Moresby",
-
"Magadan" => "Asia/Magadan",
-
"Solomon Is." => "Pacific/Guadalcanal",
-
"New Caledonia" => "Pacific/Noumea",
-
"Fiji" => "Pacific/Fiji",
-
"Kamchatka" => "Asia/Kamchatka",
-
"Marshall Is." => "Pacific/Majuro",
-
"Auckland" => "Pacific/Auckland",
-
"Wellington" => "Pacific/Auckland",
-
"Nuku'alofa" => "Pacific/Tongatapu",
-
"Tokelau Is." => "Pacific/Fakaofo",
-
"Samoa" => "Pacific/Apia"
-
}
-
-
1
UTC_OFFSET_WITH_COLON = '%s%02d:%02d'
-
1
UTC_OFFSET_WITHOUT_COLON = UTC_OFFSET_WITH_COLON.sub(':', '')
-
-
# Assumes self represents an offset from UTC in seconds (as returned from
-
# Time#utc_offset) and turns this into an +HH:MM formatted string.
-
#
-
# TimeZone.seconds_to_utc_offset(-21_600) # => "-06:00"
-
1
def self.seconds_to_utc_offset(seconds, colon = true)
-
format = colon ? UTC_OFFSET_WITH_COLON : UTC_OFFSET_WITHOUT_COLON
-
sign = (seconds < 0 ? '-' : '+')
-
hours = seconds.abs / 3600
-
minutes = (seconds.abs % 3600) / 60
-
format % [sign, hours, minutes]
-
end
-
-
1
include Comparable
-
1
attr_reader :name
-
1
attr_reader :tzinfo
-
-
# Create a new TimeZone object with the given name and offset. The
-
# offset is the number of seconds that this time zone is offset from UTC
-
# (GMT). Seconds were chosen as the offset unit because that is the unit
-
# that Ruby uses to represent time zone offsets (see Time#utc_offset).
-
1
def initialize(name, utc_offset = nil, tzinfo = nil)
-
self.class.send(:require_tzinfo)
-
-
@name = name
-
@utc_offset = utc_offset
-
@tzinfo = tzinfo || TimeZone.find_tzinfo(name)
-
@current_period = nil
-
end
-
-
# Returns the offset of this time zone from UTC in seconds.
-
1
def utc_offset
-
if @utc_offset
-
@utc_offset
-
else
-
@current_period ||= tzinfo.try(:current_period)
-
@current_period.try(:utc_offset)
-
end
-
end
-
-
# Returns the offset of this time zone as a formatted string, of the
-
# format "+HH:MM".
-
1
def formatted_offset(colon=true, alternate_utc_string = nil)
-
utc_offset == 0 && alternate_utc_string || self.class.seconds_to_utc_offset(utc_offset, colon)
-
end
-
-
# Compare this time zone to the parameter. The two are compared first on
-
# their offsets, and then by name.
-
1
def <=>(zone)
-
result = (utc_offset <=> zone.utc_offset)
-
result = (name <=> zone.name) if result == 0
-
result
-
end
-
-
# Compare #name and TZInfo identifier to a supplied regexp, returning +true+
-
# if a match is found.
-
1
def =~(re)
-
return true if name =~ re || MAPPING[name] =~ re
-
end
-
-
# Returns a textual representation of this time zone.
-
1
def to_s
-
"(GMT#{formatted_offset}) #{name}"
-
end
-
-
# Method for creating new ActiveSupport::TimeWithZone instance in time zone
-
# of +self+ from given values.
-
#
-
# Time.zone = 'Hawaii' # => "Hawaii"
-
# Time.zone.local(2007, 2, 1, 15, 30, 45) # => Thu, 01 Feb 2007 15:30:45 HST -10:00
-
1
def local(*args)
-
time = Time.utc_time(*args)
-
ActiveSupport::TimeWithZone.new(nil, self, time)
-
end
-
-
# Method for creating new ActiveSupport::TimeWithZone instance in time zone
-
# of +self+ from number of seconds since the Unix epoch.
-
#
-
# Time.zone = 'Hawaii' # => "Hawaii"
-
# Time.utc(2000).to_f # => 946684800.0
-
# Time.zone.at(946684800.0) # => Fri, 31 Dec 1999 14:00:00 HST -10:00
-
1
def at(secs)
-
Time.at(secs).utc.in_time_zone(self)
-
end
-
-
# Method for creating new ActiveSupport::TimeWithZone instance in time zone
-
# of +self+ from parsed string.
-
#
-
# Time.zone = 'Hawaii' # => "Hawaii"
-
# Time.zone.parse('1999-12-31 14:00:00') # => Fri, 31 Dec 1999 14:00:00 HST -10:00
-
#
-
# If upper components are missing from the string, they are supplied from
-
# TimeZone#now:
-
#
-
# Time.zone.now # => Fri, 31 Dec 1999 14:00:00 HST -10:00
-
# Time.zone.parse('22:30:00') # => Fri, 31 Dec 1999 22:30:00 HST -10:00
-
1
def parse(str, now=now)
-
date_parts = Date._parse(str)
-
return if date_parts.empty?
-
time = Time.parse(str, now) rescue DateTime.parse(str)
-
-
if date_parts[:offset].nil?
-
if date_parts[:hour] && time.hour != date_parts[:hour]
-
time = DateTime.parse(str)
-
end
-
-
ActiveSupport::TimeWithZone.new(nil, self, time)
-
else
-
time.in_time_zone(self)
-
end
-
end
-
-
# Returns an ActiveSupport::TimeWithZone instance representing the current
-
# time in the time zone represented by +self+.
-
#
-
# Time.zone = 'Hawaii' # => "Hawaii"
-
# Time.zone.now # => Wed, 23 Jan 2008 20:24:27 HST -10:00
-
1
def now
-
time_now.utc.in_time_zone(self)
-
end
-
-
# Return the current date in this time zone.
-
1
def today
-
tzinfo.now.to_date
-
end
-
-
# Adjust the given time to the simultaneous time in the time zone
-
# represented by +self+. Returns a Time.utc() instance -- if you want an
-
# ActiveSupport::TimeWithZone instance, use Time#in_time_zone() instead.
-
1
def utc_to_local(time)
-
tzinfo.utc_to_local(time)
-
end
-
-
# Adjust the given time to the simultaneous time in UTC. Returns a
-
# Time.utc() instance.
-
1
def local_to_utc(time, dst=true)
-
tzinfo.local_to_utc(time, dst)
-
end
-
-
# Available so that TimeZone instances respond like TZInfo::Timezone
-
# instances.
-
1
def period_for_utc(time)
-
tzinfo.period_for_utc(time)
-
end
-
-
# Available so that TimeZone instances respond like TZInfo::Timezone
-
# instances.
-
1
def period_for_local(time, dst=true)
-
tzinfo.period_for_local(time, dst)
-
end
-
-
1
def self.find_tzinfo(name)
-
TZInfo::TimezoneProxy.new(MAPPING[name] || name)
-
end
-
-
1
class << self
-
1
alias_method :create, :new
-
-
# Return a TimeZone instance with the given name, or +nil+ if no
-
# such TimeZone instance exists. (This exists to support the use of
-
# this class with the +composed_of+ macro.)
-
1
def new(name)
-
self[name]
-
end
-
-
# Return an array of all TimeZone objects. There are multiple
-
# TimeZone objects per time zone, in many cases, to make it easier
-
# for users to find their own time zone.
-
1
def all
-
@zones ||= zones_map.values.sort
-
end
-
-
1
def zones_map
-
@zones_map ||= begin
-
new_zones_names = MAPPING.keys - lazy_zones_map.keys
-
new_zones = Hash[new_zones_names.map { |place| [place, create(place)] }]
-
-
lazy_zones_map.merge(new_zones)
-
end
-
end
-
-
# Locate a specific time zone object. If the argument is a string, it
-
# is interpreted to mean the name of the timezone to locate. If it is a
-
# numeric value it is either the hour offset, or the second offset, of the
-
# timezone to find. (The first one with that offset will be returned.)
-
# Returns +nil+ if no such time zone is known to the system.
-
1
def [](arg)
-
case arg
-
when String
-
begin
-
lazy_zones_map[arg] ||= lookup(arg).tap { |tz| tz.utc_offset }
-
rescue TZInfo::InvalidTimezoneIdentifier
-
nil
-
end
-
when Numeric, ActiveSupport::Duration
-
arg *= 3600 if arg.abs <= 13
-
all.find { |z| z.utc_offset == arg.to_i }
-
else
-
raise ArgumentError, "invalid argument to TimeZone[]: #{arg.inspect}"
-
end
-
end
-
-
# A convenience method for returning a collection of TimeZone objects
-
# for time zones in the USA.
-
1
def us_zones
-
@us_zones ||= all.find_all { |z| z.name =~ /US|Arizona|Indiana|Hawaii|Alaska/ }
-
end
-
-
1
protected
-
-
1
def require_tzinfo
-
require 'tzinfo' unless defined?(::TZInfo)
-
rescue LoadError
-
$stderr.puts "You don't have tzinfo installed in your application. Please add it to your Gemfile and run bundle install"
-
raise
-
end
-
-
1
private
-
-
1
def lookup(name)
-
(tzinfo = find_tzinfo(name)) && create(tzinfo.name.freeze)
-
end
-
-
1
def lazy_zones_map
-
require_tzinfo
-
-
@lazy_zones_map ||= Hash.new do |hash, place|
-
hash[place] = create(place) if MAPPING.has_key?(place)
-
end
-
end
-
end
-
-
1
private
-
-
1
def time_now
-
Time.now
-
end
-
end
-
end
-
1
module ActiveSupport
-
1
module VERSION #:nodoc:
-
1
MAJOR = 4
-
1
MINOR = 0
-
1
TINY = 0
-
1
PRE = "beta"
-
-
1
STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
-
end
-
end
-
1
require 'time'
-
1
require 'base64'
-
1
require 'active_support/core_ext/module/delegation'
-
1
require 'active_support/core_ext/string/inflections'
-
-
1
module ActiveSupport
-
# = XmlMini
-
#
-
# To use the much faster libxml parser:
-
# gem 'libxml-ruby', '=0.9.7'
-
# XmlMini.backend = 'LibXML'
-
1
module XmlMini
-
1
extend self
-
-
# This module decorates files deserialized using Hash.from_xml with
-
# the <tt>original_filename</tt> and <tt>content_type</tt> methods.
-
1
module FileLike #:nodoc:
-
1
attr_writer :original_filename, :content_type
-
-
1
def original_filename
-
@original_filename || 'untitled'
-
end
-
-
1
def content_type
-
@content_type || 'application/octet-stream'
-
end
-
end
-
-
DEFAULT_ENCODINGS = {
-
"binary" => "base64"
-
1
} unless defined?(DEFAULT_ENCODINGS)
-
-
TYPE_NAMES = {
-
"Symbol" => "symbol",
-
"Fixnum" => "integer",
-
"Bignum" => "integer",
-
"BigDecimal" => "decimal",
-
"Float" => "float",
-
"TrueClass" => "boolean",
-
"FalseClass" => "boolean",
-
"Date" => "date",
-
"DateTime" => "dateTime",
-
"Time" => "dateTime",
-
"Array" => "array",
-
"Hash" => "hash"
-
1
} unless defined?(TYPE_NAMES)
-
-
FORMATTING = {
-
"symbol" => Proc.new { |symbol| symbol.to_s },
-
"date" => Proc.new { |date| date.to_s(:db) },
-
"dateTime" => Proc.new { |time| time.xmlschema },
-
"binary" => Proc.new { |binary| ::Base64.encode64(binary) },
-
"yaml" => Proc.new { |yaml| yaml.to_yaml }
-
1
} unless defined?(FORMATTING)
-
-
# TODO use regexp instead of Date.parse
-
1
unless defined?(PARSING)
-
1
PARSING = {
-
"symbol" => Proc.new { |symbol| symbol.to_sym },
-
"date" => Proc.new { |date| ::Date.parse(date) },
-
"datetime" => Proc.new { |time| Time.xmlschema(time).utc rescue ::DateTime.parse(time).utc },
-
"integer" => Proc.new { |integer| integer.to_i },
-
"float" => Proc.new { |float| float.to_f },
-
"decimal" => Proc.new { |number| BigDecimal(number) },
-
"boolean" => Proc.new { |boolean| %w(1 true).include?(boolean.strip) },
-
"string" => Proc.new { |string| string.to_s },
-
"yaml" => Proc.new { |yaml| YAML::load(yaml) rescue yaml },
-
"base64Binary" => Proc.new { |bin| ::Base64.decode64(bin) },
-
"binary" => Proc.new { |bin, entity| _parse_binary(bin, entity) },
-
"file" => Proc.new { |file, entity| _parse_file(file, entity) }
-
}
-
-
1
PARSING.update(
-
"double" => PARSING["float"],
-
"dateTime" => PARSING["datetime"]
-
)
-
end
-
-
1
attr_reader :backend
-
1
delegate :parse, :to => :backend
-
-
1
def backend=(name)
-
1
if name.is_a?(Module)
-
@backend = name
-
else
-
1
require "active_support/xml_mini/#{name.downcase}"
-
1
@backend = ActiveSupport.const_get("XmlMini_#{name}")
-
end
-
end
-
-
1
def with_backend(name)
-
old_backend, self.backend = backend, name
-
yield
-
ensure
-
self.backend = old_backend
-
end
-
-
1
def to_tag(key, value, options)
-
type_name = options.delete(:type)
-
merged_options = options.merge(:root => key, :skip_instruct => true)
-
-
if value.is_a?(::Method) || value.is_a?(::Proc)
-
if value.arity == 1
-
value.call(merged_options)
-
else
-
value.call(merged_options, key.to_s.singularize)
-
end
-
elsif value.respond_to?(:to_xml)
-
value.to_xml(merged_options)
-
else
-
type_name ||= TYPE_NAMES[value.class.name]
-
type_name ||= value.class.name if value && !value.respond_to?(:to_str)
-
type_name = type_name.to_s if type_name
-
type_name = "dateTime" if type_name == "datetime"
-
-
key = rename_key(key.to_s, options)
-
-
attributes = options[:skip_types] || type_name.nil? ? { } : { :type => type_name }
-
attributes[:nil] = true if value.nil?
-
-
encoding = options[:encoding] || DEFAULT_ENCODINGS[type_name]
-
attributes[:encoding] = encoding if encoding
-
-
formatted_value = FORMATTING[type_name] && !value.nil? ?
-
FORMATTING[type_name].call(value) : value
-
-
options[:builder].tag!(key, formatted_value, attributes)
-
end
-
end
-
-
1
def rename_key(key, options = {})
-
camelize = options[:camelize]
-
dasherize = !options.has_key?(:dasherize) || options[:dasherize]
-
if camelize
-
key = true == camelize ? key.camelize : key.camelize(camelize)
-
end
-
key = _dasherize(key) if dasherize
-
key
-
end
-
-
1
protected
-
-
1
def _dasherize(key)
-
# $2 must be a non-greedy regex for this to work
-
left, middle, right = /\A(_*)(.*?)(_*)\Z/.match(key.strip)[1,3]
-
"#{left}#{middle.tr('_ ', '--')}#{right}"
-
end
-
-
# TODO: Add support for other encodings
-
1
def _parse_binary(bin, entity) #:nodoc:
-
case entity['encoding']
-
when 'base64'
-
::Base64.decode64(bin)
-
else
-
bin
-
end
-
end
-
-
1
def _parse_file(file, entity)
-
f = StringIO.new(::Base64.decode64(file))
-
f.extend(FileLike)
-
f.original_filename = entity['name']
-
f.content_type = entity['content_type']
-
f
-
end
-
end
-
-
1
XmlMini.backend = 'REXML'
-
end
-
1
require 'active_support/core_ext/kernel/reporting'
-
1
require 'active_support/core_ext/object/blank'
-
1
require 'stringio'
-
-
1
module ActiveSupport
-
1
module XmlMini_REXML #:nodoc:
-
1
extend self
-
-
1
CONTENT_KEY = '__content__'.freeze
-
-
# Parse an XML Document string or IO into a simple hash.
-
#
-
# Same as XmlSimple::xml_in but doesn't shoot itself in the foot,
-
# and uses the defaults from Active Support.
-
#
-
# data::
-
# XML Document string or IO to parse
-
1
def parse(data)
-
if !data.respond_to?(:read)
-
data = StringIO.new(data || '')
-
end
-
-
char = data.getc
-
if char.nil?
-
{}
-
else
-
data.ungetc(char)
-
silence_warnings { require 'rexml/document' } unless defined?(REXML::Document)
-
doc = REXML::Document.new(data)
-
-
if doc.root
-
merge_element!({}, doc.root)
-
else
-
raise REXML::ParseException,
-
"The document #{doc.to_s.inspect} does not have a valid root"
-
end
-
end
-
end
-
-
1
private
-
# Convert an XML element and merge into the hash
-
#
-
# hash::
-
# Hash to merge the converted element into.
-
# element::
-
# XML element to merge into hash
-
1
def merge_element!(hash, element)
-
merge!(hash, element.name, collapse(element))
-
end
-
-
# Actually converts an XML document element into a data structure.
-
#
-
# element::
-
# The document element to be collapsed.
-
1
def collapse(element)
-
hash = get_attributes(element)
-
-
if element.has_elements?
-
element.each_element {|child| merge_element!(hash, child) }
-
merge_texts!(hash, element) unless empty_content?(element)
-
hash
-
else
-
merge_texts!(hash, element)
-
end
-
end
-
-
# Merge all the texts of an element into the hash
-
#
-
# hash::
-
# Hash to add the converted element to.
-
# element::
-
# XML element whose texts are to me merged into the hash
-
1
def merge_texts!(hash, element)
-
unless element.has_text?
-
hash
-
else
-
# must use value to prevent double-escaping
-
texts = ''
-
element.texts.each { |t| texts << t.value }
-
merge!(hash, CONTENT_KEY, texts)
-
end
-
end
-
-
# Adds a new key/value pair to an existing Hash. If the key to be added
-
# already exists and the existing value associated with key is not
-
# an Array, it will be wrapped in an Array. Then the new value is
-
# appended to that Array.
-
#
-
# hash::
-
# Hash to add key/value pair to.
-
# key::
-
# Key to be added.
-
# value::
-
# Value to be associated with key.
-
1
def merge!(hash, key, value)
-
if hash.has_key?(key)
-
if hash[key].instance_of?(Array)
-
hash[key] << value
-
else
-
hash[key] = [hash[key], value]
-
end
-
elsif value.instance_of?(Array)
-
hash[key] = [value]
-
else
-
hash[key] = value
-
end
-
hash
-
end
-
-
# Converts the attributes array of an XML element into a hash.
-
# Returns an empty Hash if node has no attributes.
-
#
-
# element::
-
# XML element to extract attributes from.
-
1
def get_attributes(element)
-
attributes = {}
-
element.attributes.each { |n,v| attributes[n] = v }
-
attributes
-
end
-
-
# Determines if a document element has text content
-
#
-
# element::
-
# XML element to be checked.
-
1
def empty_content?(element)
-
element.texts.join.blank?
-
end
-
end
-
end
-
# bust gem prelude
-
1
require 'bundler'
-
1
Bundler.setup
-
##
-
## $Release: 2.7.0 $
-
## copyright(c) 2006-2011 kuwata-lab.com all rights reserved.
-
##
-
-
##
-
## an implementation of eRuby
-
##
-
## ex.
-
## input = <<'END'
-
## <ul>
-
## <% for item in @list %>
-
## <li><%= item %>
-
## <%== item %></li>
-
## <% end %>
-
## </ul>
-
## END
-
## list = ['<aaa>', 'b&b', '"ccc"']
-
## eruby = Erubis::Eruby.new(input)
-
## puts "--- code ---"
-
## puts eruby.src
-
## puts "--- result ---"
-
## context = Erubis::Context.new() # or new(:list=>list)
-
## context[:list] = list
-
## puts eruby.evaluate(context)
-
##
-
## result:
-
## --- source ---
-
## _buf = ''; _buf << '<ul>
-
## '; for item in @list
-
## _buf << ' <li>'; _buf << ( item ).to_s; _buf << '
-
## '; _buf << ' '; _buf << Erubis::XmlHelper.escape_xml( item ); _buf << '</li>
-
## '; end
-
## _buf << '</ul>
-
## ';
-
## _buf.to_s
-
## --- result ---
-
## <ul>
-
## <li><aaa>
-
## <aaa></li>
-
## <li>b&b
-
## b&b</li>
-
## <li>"ccc"
-
## "ccc"</li>
-
## </ul>
-
##
-
-
-
1
module Erubis
-
1
VERSION = ('$Release: 2.7.0 $' =~ /([.\d]+)/) && $1
-
end
-
-
1
require 'erubis/engine'
-
#require 'erubis/generator'
-
#require 'erubis/converter'
-
#require 'erubis/evaluator'
-
#require 'erubis/error'
-
#require 'erubis/context'
-
#requier 'erubis/util'
-
1
require 'erubis/helper'
-
1
require 'erubis/enhancer'
-
#require 'erubis/tiny'
-
1
require 'erubis/engine/eruby'
-
#require 'erubis/engine/enhanced' # enhanced eruby engines
-
#require 'erubis/engine/optimized' # generates optimized ruby code
-
#require 'erubis/engine/ephp'
-
#require 'erubis/engine/ec'
-
#require 'erubis/engine/ejava'
-
#require 'erubis/engine/escheme'
-
#require 'erubis/engine/eperl'
-
#require 'erubis/engine/ejavascript'
-
-
1
require 'erubis/local-setting'
-
##
-
## $Release: 2.7.0 $
-
## copyright(c) 2006-2011 kuwata-lab.com all rights reserved.
-
##
-
-
-
1
module Erubis
-
-
-
##
-
## context object for Engine#evaluate
-
##
-
## ex.
-
## template = <<'END'
-
## Hello <%= @user %>!
-
## <% for item in @list %>
-
## - <%= item %>
-
## <% end %>
-
## END
-
##
-
## context = Erubis::Context.new(:user=>'World', :list=>['a','b','c'])
-
## # or
-
## # context = Erubis::Context.new
-
## # context[:user] = 'World'
-
## # context[:list] = ['a', 'b', 'c']
-
##
-
## eruby = Erubis::Eruby.new(template)
-
## print eruby.evaluate(context)
-
##
-
1
class Context
-
1
include Enumerable
-
-
1
def initialize(hash=nil)
-
hash.each do |name, value|
-
self[name] = value
-
end if hash
-
end
-
-
1
def [](key)
-
return instance_variable_get("@#{key}")
-
end
-
-
1
def []=(key, value)
-
return instance_variable_set("@#{key}", value)
-
end
-
-
1
def keys
-
return instance_variables.collect { |name| name[1..-1] }
-
end
-
-
1
def each
-
instance_variables.each do |name|
-
key = name[1..-1]
-
value = instance_variable_get(name)
-
yield(key, value)
-
end
-
end
-
-
1
def to_hash
-
hash = {}
-
self.keys.each { |key| hash[key] = self[key] }
-
return hash
-
end
-
-
1
def update(context_or_hash)
-
arg = context_or_hash
-
if arg.is_a?(Hash)
-
arg.each do |key, val|
-
self[key] = val
-
end
-
else
-
arg.instance_variables.each do |varname|
-
key = varname[1..-1]
-
val = arg.instance_variable_get(varname)
-
self[key] = val
-
end
-
end
-
end
-
-
end
-
-
-
end
-
##
-
## $Release: 2.7.0 $
-
## copyright(c) 2006-2011 kuwata-lab.com all rights reserved.
-
##
-
-
1
require 'erubis/util'
-
-
1
module Erubis
-
-
-
##
-
## convert
-
##
-
1
module Converter
-
-
1
attr_accessor :preamble, :postamble, :escape
-
-
1
def self.supported_properties # :nodoc:
-
return [
-
[:preamble, nil, "preamble (no preamble when false)"],
-
[:postamble, nil, "postamble (no postamble when false)"],
-
[:escape, nil, "escape expression or not in default"],
-
]
-
end
-
-
1
def init_converter(properties={})
-
66
@preamble = properties[:preamble]
-
66
@postamble = properties[:postamble]
-
66
@escape = properties[:escape]
-
end
-
-
## convert input string into target language
-
1
def convert(input)
-
66
codebuf = "" # or []
-
66
@preamble.nil? ? add_preamble(codebuf) : (@preamble && (codebuf << @preamble))
-
66
convert_input(codebuf, input)
-
66
@postamble.nil? ? add_postamble(codebuf) : (@postamble && (codebuf << @postamble))
-
66
@_proc = nil # clear cached proc object
-
66
return codebuf # or codebuf.join()
-
end
-
-
1
protected
-
-
##
-
## detect spaces at beginning of line
-
##
-
1
def detect_spaces_at_bol(text, is_bol)
-
lspace = nil
-
if text.empty?
-
lspace = "" if is_bol
-
elsif text[-1] == ?\n
-
lspace = ""
-
else
-
rindex = text.rindex(?\n)
-
if rindex
-
s = text[rindex+1..-1]
-
if s =~ /\A[ \t]*\z/
-
lspace = s
-
#text = text[0..rindex]
-
text[rindex+1..-1] = ''
-
end
-
else
-
if is_bol && text =~ /\A[ \t]*\z/
-
#lspace = text
-
#text = nil
-
lspace = text.dup
-
text[0..-1] = ''
-
end
-
end
-
end
-
return lspace
-
end
-
-
##
-
## (abstract) convert input to code
-
##
-
1
def convert_input(codebuf, input)
-
not_implemented
-
end
-
-
end
-
-
-
1
module Basic
-
end
-
-
-
##
-
## basic converter which supports '<% ... %>' notation.
-
##
-
1
module Basic::Converter
-
1
include Erubis::Converter
-
-
1
def self.supported_properties # :nodoc:
-
return [
-
[:pattern, '<% %>', "embed pattern"],
-
[:trim, true, "trim spaces around <% ... %>"],
-
]
-
end
-
-
1
attr_accessor :pattern, :trim
-
-
1
def init_converter(properties={})
-
66
super(properties)
-
66
@pattern = properties[:pattern]
-
66
@trim = properties[:trim] != false
-
end
-
-
1
protected
-
-
## return regexp of pattern to parse eRuby script
-
1
def pattern_regexp(pattern)
-
1
@prefix, @postfix = pattern.split() # '<% %>' => '<%', '%>'
-
#return /(.*?)(^[ \t]*)?#{@prefix}(=+|\#)?(.*?)-?#{@postfix}([ \t]*\r?\n)?/m
-
#return /(^[ \t]*)?#{@prefix}(=+|\#)?(.*?)-?#{@postfix}([ \t]*\r?\n)?/m
-
1
return /#{@prefix}(=+|-|\#|%)?(.*?)([-=])?#{@postfix}([ \t]*\r?\n)?/m
-
end
-
1
module_function :pattern_regexp
-
-
#DEFAULT_REGEXP = /(.*?)(^[ \t]*)?<%(=+|\#)?(.*?)-?%>([ \t]*\r?\n)?/m
-
#DEFAULT_REGEXP = /(^[ \t]*)?<%(=+|\#)?(.*?)-?%>([ \t]*\r?\n)?/m
-
#DEFAULT_REGEXP = /<%(=+|\#)?(.*?)-?%>([ \t]*\r?\n)?/m
-
1
DEFAULT_REGEXP = pattern_regexp('<% %>')
-
-
1
public
-
-
1
def convert_input(src, input)
-
66
pat = @pattern
-
66
regexp = pat.nil? || pat == '<% %>' ? DEFAULT_REGEXP : pattern_regexp(pat)
-
66
pos = 0
-
66
is_bol = true # is beginning of line
-
66
input.scan(regexp) do |indicator, code, tailch, rspace|
-
35
match = Regexp.last_match()
-
35
len = match.begin(0) - pos
-
35
text = input[pos, len]
-
35
pos = match.end(0)
-
35
ch = indicator ? indicator[0] : nil
-
35
lspace = ch == ?= ? nil : detect_spaces_at_bol(text, is_bol)
-
35
is_bol = rspace ? true : false
-
35
add_text(src, text) if text && !text.empty?
-
## * when '<%= %>', do nothing
-
## * when '<% %>' or '<%# %>', delete spaces iff only spaces are around '<% %>'
-
35
if ch == ?= # <%= %>
-
35
rspace = nil if tailch && !tailch.empty?
-
35
add_text(src, lspace) if lspace
-
35
add_expr(src, code, indicator)
-
35
add_text(src, rspace) if rspace
-
elsif ch == ?\# # <%# %>
-
n = code.count("\n") + (rspace ? 1 : 0)
-
if @trim && lspace && rspace
-
add_stmt(src, "\n" * n)
-
else
-
add_text(src, lspace) if lspace
-
add_stmt(src, "\n" * n)
-
add_text(src, rspace) if rspace
-
end
-
elsif ch == ?% # <%% %>
-
s = "#{lspace}#{@prefix||='<%'}#{code}#{tailch}#{@postfix||='%>'}#{rspace}"
-
add_text(src, s)
-
else # <% %>
-
if @trim && lspace && rspace
-
add_stmt(src, "#{lspace}#{code}#{rspace}")
-
else
-
add_text(src, lspace) if lspace
-
add_stmt(src, code)
-
add_text(src, rspace) if rspace
-
end
-
end
-
end
-
#rest = $' || input # ruby1.8
-
66
rest = pos == 0 ? input : input[pos..-1] # ruby1.9
-
66
add_text(src, rest)
-
end
-
-
## add expression code to src
-
1
def add_expr(src, code, indicator)
-
35
case indicator
-
when '='
-
35
@escape ? add_expr_escaped(src, code) : add_expr_literal(src, code)
-
when '=='
-
@escape ? add_expr_literal(src, code) : add_expr_escaped(src, code)
-
when '==='
-
add_expr_debug(src, code)
-
end
-
end
-
-
end
-
-
-
1
module PI
-
end
-
-
##
-
## Processing Instructions (PI) converter for XML.
-
## this class converts '<?rb ... ?>' and '${...}' notation.
-
##
-
1
module PI::Converter
-
1
include Erubis::Converter
-
-
1
def self.desc # :nodoc:
-
"use processing instructions (PI) instead of '<% %>'"
-
end
-
-
1
def self.supported_properties # :nodoc:
-
return [
-
[:trim, true, "trim spaces around <% ... %>"],
-
[:pi, 'rb', "PI (Processing Instrunctions) name"],
-
[:embchar, '@', "char for embedded expression pattern('@{...}@')"],
-
[:pattern, '<% %>', "embed pattern"],
-
]
-
end
-
-
1
attr_accessor :pi, :prefix
-
-
1
def init_converter(properties={})
-
super(properties)
-
@trim = properties.fetch(:trim, true)
-
@pi = properties[:pi] if properties[:pi]
-
@embchar = properties[:embchar] || '@'
-
@pattern = properties[:pattern]
-
@pattern = '<% %>' if @pattern.nil? #|| @pattern == true
-
end
-
-
1
def convert(input)
-
code = super(input)
-
return @header || @footer ? "#{@header}#{code}#{@footer}" : code
-
end
-
-
1
protected
-
-
1
def convert_input(codebuf, input)
-
unless @regexp
-
@pi ||= 'e'
-
ch = Regexp.escape(@embchar)
-
if @pattern
-
left, right = @pattern.split(' ')
-
@regexp = /<\?#{@pi}(?:-(\w+))?(\s.*?)\?>([ \t]*\r?\n)?|#{ch}(!*)?\{(.*?)\}#{ch}|#{left}(=+)(.*?)#{right}/m
-
else
-
@regexp = /<\?#{@pi}(?:-(\w+))?(\s.*?)\?>([ \t]*\r?\n)?|#{ch}(!*)?\{(.*?)\}#{ch}/m
-
end
-
end
-
#
-
is_bol = true
-
pos = 0
-
input.scan(@regexp) do |pi_arg, stmt, rspace,
-
indicator1, expr1, indicator2, expr2|
-
match = Regexp.last_match
-
len = match.begin(0) - pos
-
text = input[pos, len]
-
pos = match.end(0)
-
lspace = stmt ? detect_spaces_at_bol(text, is_bol) : nil
-
is_bol = stmt && rspace ? true : false
-
add_text(codebuf, text) # unless text.empty?
-
#
-
if stmt
-
if @trim && lspace && rspace
-
add_pi_stmt(codebuf, "#{lspace}#{stmt}#{rspace}", pi_arg)
-
else
-
add_text(codebuf, lspace) if lspace
-
add_pi_stmt(codebuf, stmt, pi_arg)
-
add_text(codebuf, rspace) if rspace
-
end
-
else
-
add_pi_expr(codebuf, expr1 || expr2, indicator1 || indicator2)
-
end
-
end
-
#rest = $' || input # ruby1.8
-
rest = pos == 0 ? input : input[pos..-1] # ruby1.9
-
add_text(codebuf, rest)
-
end
-
-
#--
-
#def convert_input(codebuf, input)
-
# parse_stmts(codebuf, input)
-
# #parse_stmts2(codebuf, input)
-
#end
-
#
-
#def parse_stmts(codebuf, input)
-
# #regexp = pattern_regexp(@pattern)
-
# @pi ||= 'e'
-
# @stmt_pattern ||= /<\?#{@pi}(?:-(\w+))?(\s.*?)\?>([ \t]*\r?\n)?/m
-
# is_bol = true
-
# pos = 0
-
# input.scan(@stmt_pattern) do |pi_arg, code, rspace|
-
# match = Regexp.last_match
-
# len = match.begin(0) - pos
-
# text = input[pos, len]
-
# pos = match.end(0)
-
# lspace = detect_spaces_at_bol(text, is_bol)
-
# is_bol = rspace ? true : false
-
# parse_exprs(codebuf, text) # unless text.empty?
-
# if @trim && lspace && rspace
-
# add_pi_stmt(codebuf, "#{lspace}#{code}#{rspace}", pi_arg)
-
# else
-
# add_text(codebuf, lspace)
-
# add_pi_stmt(codebuf, code, pi_arg)
-
# add_text(codebuf, rspace)
-
# end
-
# end
-
# rest = $' || input
-
# parse_exprs(codebuf, rest)
-
#end
-
#
-
#def parse_exprs(codebuf, input)
-
# unless @expr_pattern
-
# ch = Regexp.escape(@embchar)
-
# if @pattern
-
# left, right = @pattern.split(' ')
-
# @expr_pattern = /#{ch}(!*)?\{(.*?)\}#{ch}|#{left}(=+)(.*?)#{right}/
-
# else
-
# @expr_pattern = /#{ch}(!*)?\{(.*?)\}#{ch}/
-
# end
-
# end
-
# pos = 0
-
# input.scan(@expr_pattern) do |indicator1, code1, indicator2, code2|
-
# indicator = indicator1 || indicator2
-
# code = code1 || code2
-
# match = Regexp.last_match
-
# len = match.begin(0) - pos
-
# text = input[pos, len]
-
# pos = match.end(0)
-
# add_text(codebuf, text) # unless text.empty?
-
# add_pi_expr(codebuf, code, indicator)
-
# end
-
# rest = $' || input
-
# add_text(codebuf, rest)
-
#end
-
#++
-
-
1
def add_pi_stmt(codebuf, code, pi_arg) # :nodoc:
-
case pi_arg
-
when nil ; add_stmt(codebuf, code)
-
when 'header' ; @header = code
-
when 'footer' ; @footer = code
-
when 'comment'; add_stmt(codebuf, "\n" * code.count("\n"))
-
when 'value' ; add_expr_literal(codebuf, code)
-
else ; add_stmt(codebuf, code)
-
end
-
end
-
-
1
def add_pi_expr(codebuf, code, indicator) # :nodoc:
-
case indicator
-
when nil, '', '==' # @{...}@ or <%== ... %>
-
@escape == false ? add_expr_literal(codebuf, code) : add_expr_escaped(codebuf, code)
-
when '!', '=' # @!{...}@ or <%= ... %>
-
@escape == false ? add_expr_escaped(codebuf, code) : add_expr_literal(codebuf, code)
-
when '!!', '===' # @!!{...}@ or <%=== ... %>
-
add_expr_debug(codebuf, code)
-
else
-
# ignore
-
end
-
end
-
-
end
-
-
-
end
-
##
-
## $Release: 2.7.0 $
-
## copyright(c) 2006-2011 kuwata-lab.com all rights reserved.
-
##
-
-
-
1
require 'erubis/generator'
-
1
require 'erubis/converter'
-
1
require 'erubis/evaluator'
-
1
require 'erubis/context'
-
-
-
1
module Erubis
-
-
-
##
-
## (abstract) abstract engine class.
-
## subclass must include evaluator and converter module.
-
##
-
1
class Engine
-
#include Evaluator
-
#include Converter
-
#include Generator
-
-
1
def initialize(input=nil, properties={})
-
#@input = input
-
66
init_generator(properties)
-
66
init_converter(properties)
-
66
init_evaluator(properties)
-
66
@src = convert(input) if input
-
end
-
-
-
##
-
## convert input string and set it to @src
-
##
-
1
def convert!(input)
-
@src = convert(input)
-
end
-
-
-
##
-
## load file, write cache file, and return engine object.
-
## this method create code cache file automatically.
-
## cachefile name can be specified with properties[:cachename],
-
## or filname + 'cache' is used as default.
-
##
-
1
def self.load_file(filename, properties={})
-
cachename = properties[:cachename] || (filename + '.cache')
-
properties[:filename] = filename
-
timestamp = File.mtime(filename)
-
if test(?f, cachename) && timestamp == File.mtime(cachename)
-
engine = self.new(nil, properties)
-
engine.src = File.read(cachename)
-
else
-
input = File.open(filename, 'rb') {|f| f.read }
-
engine = self.new(input, properties)
-
tmpname = cachename + rand().to_s[1,8]
-
File.open(tmpname, 'wb') {|f| f.write(engine.src) }
-
File.rename(tmpname, cachename)
-
File.utime(timestamp, timestamp, cachename)
-
end
-
engine.src.untaint # ok?
-
return engine
-
end
-
-
-
##
-
## helper method to convert and evaluate input text with context object.
-
## context may be Binding, Hash, or Object.
-
##
-
1
def process(input, context=nil, filename=nil)
-
code = convert(input)
-
filename ||= '(erubis)'
-
if context.is_a?(Binding)
-
return eval(code, context, filename)
-
else
-
context = Context.new(context) if context.is_a?(Hash)
-
return context.instance_eval(code, filename)
-
end
-
end
-
-
-
##
-
## helper method evaluate Proc object with contect object.
-
## context may be Binding, Hash, or Object.
-
##
-
1
def process_proc(proc_obj, context=nil, filename=nil)
-
if context.is_a?(Binding)
-
filename ||= '(erubis)'
-
return eval(proc_obj, context, filename)
-
else
-
context = Context.new(context) if context.is_a?(Hash)
-
return context.instance_eval(&proc_obj)
-
end
-
end
-
-
-
end # end of class Engine
-
-
-
##
-
## (abstract) base engine class for Eruby, Eperl, Ejava, and so on.
-
## subclass must include generator.
-
##
-
1
class Basic::Engine < Engine
-
1
include Evaluator
-
1
include Basic::Converter
-
1
include Generator
-
end
-
-
-
1
class PI::Engine < Engine
-
1
include Evaluator
-
1
include PI::Converter
-
1
include Generator
-
end
-
-
-
end
-
##
-
## $Release: 2.7.0 $
-
## copyright(c) 2006-2011 kuwata-lab.com all rights reserved.
-
##
-
-
-
1
module Erubis
-
-
-
##
-
## switch '<%= ... %>' to escaped and '<%== ... %>' to unescaped
-
##
-
## ex.
-
## class XmlEruby < Eruby
-
## include EscapeEnhancer
-
## end
-
##
-
## this is language-indenedent.
-
##
-
1
module EscapeEnhancer
-
-
1
def self.desc # :nodoc:
-
"switch '<%= %>' to escaped and '<%== %>' to unescaped"
-
end
-
-
#--
-
#def self.included(klass)
-
# klass.class_eval <<-END
-
# alias _add_expr_literal add_expr_literal
-
# alias _add_expr_escaped add_expr_escaped
-
# alias add_expr_literal _add_expr_escaped
-
# alias add_expr_escaped _add_expr_literal
-
# END
-
#end
-
#++
-
-
1
def add_expr(src, code, indicator)
-
case indicator
-
when '='
-
@escape ? add_expr_literal(src, code) : add_expr_escaped(src, code)
-
when '=='
-
@escape ? add_expr_escaped(src, code) : add_expr_literal(src, code)
-
when '==='
-
add_expr_debug(src, code)
-
end
-
end
-
-
end
-
-
-
#--
-
## (obsolete)
-
#module FastEnhancer
-
#end
-
#++
-
-
-
##
-
## use $stdout instead of string
-
##
-
## this is only for Eruby.
-
##
-
1
module StdoutEnhancer
-
-
1
def self.desc # :nodoc:
-
"use $stdout instead of array buffer or string buffer"
-
end
-
-
1
def add_preamble(src)
-
src << "#{@bufvar} = $stdout;"
-
end
-
-
1
def add_postamble(src)
-
src << "\n''\n"
-
end
-
-
end
-
-
-
##
-
## use print statement instead of '_buf << ...'
-
##
-
## this is only for Eruby.
-
##
-
1
module PrintOutEnhancer
-
-
1
def self.desc # :nodoc:
-
"use print statement instead of '_buf << ...'"
-
end
-
-
1
def add_preamble(src)
-
end
-
-
1
def add_text(src, text)
-
src << " print '#{escape_text(text)}';" unless text.empty?
-
end
-
-
1
def add_expr_literal(src, code)
-
src << " print((#{code}).to_s);"
-
end
-
-
1
def add_expr_escaped(src, code)
-
src << " print #{escaped_expr(code)};"
-
end
-
-
1
def add_postamble(src)
-
src << "\n" unless src[-1] == ?\n
-
end
-
-
end
-
-
-
##
-
## enable print function
-
##
-
## Notice: use Eruby#evaluate() and don't use Eruby#result()
-
## to be enable print function.
-
##
-
## this is only for Eruby.
-
##
-
1
module PrintEnabledEnhancer
-
-
1
def self.desc # :nodoc:
-
"enable to use print function in '<% %>'"
-
end
-
-
1
def add_preamble(src)
-
src << "@_buf = "
-
super
-
end
-
-
1
def print(*args)
-
args.each do |arg|
-
@_buf << arg.to_s
-
end
-
end
-
-
1
def evaluate(context=nil)
-
_src = @src
-
if context.is_a?(Hash)
-
context.each do |key, val| instance_variable_set("@#{key}", val) end
-
elsif context
-
context.instance_variables.each do |name|
-
instance_variable_set(name, context.instance_variable_get(name))
-
end
-
end
-
return instance_eval(_src, (@filename || '(erubis)'))
-
end
-
-
end
-
-
-
##
-
## return array instead of string
-
##
-
## this is only for Eruby.
-
##
-
1
module ArrayEnhancer
-
-
1
def self.desc # :nodoc:
-
"return array instead of string"
-
end
-
-
1
def add_preamble(src)
-
src << "#{@bufvar} = [];"
-
end
-
-
1
def add_postamble(src)
-
src << "\n" unless src[-1] == ?\n
-
src << "#{@bufvar}\n"
-
end
-
-
end
-
-
-
##
-
## use an Array object as buffer (included in Eruby by default)
-
##
-
## this is only for Eruby.
-
##
-
1
module ArrayBufferEnhancer
-
-
1
def self.desc # :nodoc:
-
"use an Array object for buffering (included in Eruby class)"
-
end
-
-
1
def add_preamble(src)
-
src << "_buf = [];"
-
end
-
-
1
def add_postamble(src)
-
src << "\n" unless src[-1] == ?\n
-
src << "_buf.join\n"
-
end
-
-
end
-
-
-
##
-
## use String class for buffering
-
##
-
## this is only for Eruby.
-
##
-
1
module StringBufferEnhancer
-
-
1
def self.desc # :nodoc:
-
"use a String object for buffering"
-
end
-
-
1
def add_preamble(src)
-
src << "#{@bufvar} = '';"
-
end
-
-
1
def add_postamble(src)
-
src << "\n" unless src[-1] == ?\n
-
src << "#{@bufvar}.to_s\n"
-
end
-
-
end
-
-
-
##
-
## use StringIO class for buffering
-
##
-
## this is only for Eruby.
-
##
-
1
module StringIOEnhancer # :nodoc:
-
-
1
def self.desc # :nodoc:
-
"use a StringIO object for buffering"
-
end
-
-
1
def add_preamble(src)
-
src << "#{@bufvar} = StringIO.new;"
-
end
-
-
1
def add_postamble(src)
-
src << "\n" unless src[-1] == ?\n
-
src << "#{@bufvar}.string\n"
-
end
-
-
end
-
-
-
##
-
## set buffer variable name to '_erbout' as well as '_buf'
-
##
-
## this is only for Eruby.
-
##
-
1
module ErboutEnhancer
-
-
1
def self.desc # :nodoc:
-
"set '_erbout = _buf = \"\";' to be compatible with ERB."
-
end
-
-
1
def add_preamble(src)
-
src << "_erbout = #{@bufvar} = '';"
-
end
-
-
1
def add_postamble(src)
-
src << "\n" unless src[-1] == ?\n
-
src << "#{@bufvar}.to_s\n"
-
end
-
-
end
-
-
-
##
-
## remove text and leave code, especially useful when debugging.
-
##
-
## ex.
-
## $ erubis -s -E NoText file.eruby | more
-
##
-
## this is language independent.
-
##
-
1
module NoTextEnhancer
-
-
1
def self.desc # :nodoc:
-
"remove text and leave code (useful when debugging)"
-
end
-
-
1
def add_text(src, text)
-
src << ("\n" * text.count("\n"))
-
if text[-1] != ?\n
-
text =~ /^(.*?)\z/
-
src << (' ' * $1.length)
-
end
-
end
-
-
end
-
-
-
##
-
## remove code and leave text, especially useful when validating HTML tags.
-
##
-
## ex.
-
## $ erubis -s -E NoCode file.eruby | tidy -errors
-
##
-
## this is language independent.
-
##
-
1
module NoCodeEnhancer
-
-
1
def self.desc # :nodoc:
-
"remove code and leave text (useful when validating HTML)"
-
end
-
-
1
def add_preamble(src)
-
end
-
-
1
def add_postamble(src)
-
end
-
-
1
def add_text(src, text)
-
src << text
-
end
-
-
1
def add_expr(src, code, indicator)
-
src << "\n" * code.count("\n")
-
end
-
-
1
def add_stmt(src, code)
-
src << "\n" * code.count("\n")
-
end
-
-
end
-
-
-
##
-
## get convert faster, but spaces around '<%...%>' are not trimmed.
-
##
-
## this is language-independent.
-
##
-
1
module SimplifyEnhancer
-
-
1
def self.desc # :nodoc:
-
"get convert faster but leave spaces around '<% %>'"
-
end
-
-
#DEFAULT_REGEXP = /(^[ \t]*)?<%(=+|\#)?(.*?)-?%>([ \t]*\r?\n)?/m
-
1
SIMPLE_REGEXP = /<%(=+|\#)?(.*?)-?%>/m
-
-
1
def convert(input)
-
src = ""
-
add_preamble(src)
-
#regexp = pattern_regexp(@pattern)
-
pos = 0
-
input.scan(SIMPLE_REGEXP) do |indicator, code|
-
match = Regexp.last_match
-
index = match.begin(0)
-
text = input[pos, index - pos]
-
pos = match.end(0)
-
add_text(src, text)
-
if !indicator # <% %>
-
add_stmt(src, code)
-
elsif indicator[0] == ?\# # <%# %>
-
n = code.count("\n")
-
add_stmt(src, "\n" * n)
-
else # <%= %>
-
add_expr(src, code, indicator)
-
end
-
end
-
#rest = $' || input # ruby1.8
-
rest = pos == 0 ? input : input[pos..-1] # ruby1.9
-
add_text(src, rest)
-
add_postamble(src)
-
return src
-
end
-
-
end
-
-
-
##
-
## enable to use other embedded expression pattern (default is '\[= =\]').
-
##
-
## notice! this is an experimental. spec may change in the future.
-
##
-
## ex.
-
## input = <<END
-
## <% for item in list %>
-
## <%= item %> : <%== item %>
-
## [= item =] : [== item =]
-
## <% end %>
-
## END
-
##
-
## class BiPatternEruby
-
## include BiPatternEnhancer
-
## end
-
## eruby = BiPatternEruby.new(input, :bipattern=>'\[= =\]')
-
## list = ['<a>', 'b&b', '"c"']
-
## print eruby.result(binding())
-
##
-
## ## output
-
## <a> : <a>
-
## <a> : <a>
-
## b&b : b&b
-
## b&b : b&b
-
## "c" : "c"
-
## "c" : "c"
-
##
-
## this is language independent.
-
##
-
1
module BiPatternEnhancer
-
-
1
def self.desc # :nodoc:
-
"another embedded expression pattern (default '\[= =\]')."
-
end
-
-
1
def initialize(input, properties={})
-
self.bipattern = properties[:bipattern] # or '\$\{ \}'
-
super
-
end
-
-
## when pat is nil then '\[= =\]' is used
-
1
def bipattern=(pat) # :nodoc:
-
@bipattern = pat || '\[= =\]'
-
pre, post = @bipattern.split()
-
@bipattern_regexp = /(.*?)#{pre}(=*)(.*?)#{post}/m
-
end
-
-
1
def add_text(src, text)
-
return unless text
-
m = nil
-
text.scan(@bipattern_regexp) do |txt, indicator, code|
-
m = Regexp.last_match
-
super(src, txt)
-
add_expr(src, code, '=' + indicator)
-
end
-
#rest = $' || text # ruby1.8
-
rest = m ? text[m.end(0)..-1] : text # ruby1.9
-
super(src, rest)
-
end
-
-
end
-
-
-
##
-
## regards lines starting with '^[ \t]*%' as program code
-
##
-
## in addition you can specify prefix character (default '%')
-
##
-
## this is language-independent.
-
##
-
1
module PrefixedLineEnhancer
-
-
1
def self.desc # :nodoc:
-
"regard lines matched to '^[ \t]*%' as program code"
-
end
-
-
1
def init_generator(properties={})
-
super
-
@prefixchar = properties[:prefixchar]
-
end
-
-
1
def add_text(src, text)
-
unless @prefixrexp
-
@prefixchar ||= '%'
-
@prefixrexp = Regexp.compile("^([ \\t]*)\\#{@prefixchar}(.*?\\r?\\n)")
-
end
-
pos = 0
-
text2 = ''
-
text.scan(@prefixrexp) do
-
space = $1
-
line = $2
-
space, line = '', $1 unless $2
-
match = Regexp.last_match
-
len = match.begin(0) - pos
-
str = text[pos, len]
-
pos = match.end(0)
-
if text2.empty?
-
text2 = str
-
else
-
text2 << str
-
end
-
if line[0, 1] == @prefixchar
-
text2 << space << line
-
else
-
super(src, text2)
-
text2 = ''
-
add_stmt(src, space + line)
-
end
-
end
-
#rest = pos == 0 ? text : $' # ruby1.8
-
rest = pos == 0 ? text : text[pos..-1] # ruby1.9
-
unless text2.empty?
-
text2 << rest if rest
-
rest = text2
-
end
-
super(src, rest)
-
end
-
-
end
-
-
-
##
-
## regards lines starting with '%' as program code
-
##
-
## this is for compatibility to eruby and ERB.
-
##
-
## this is language-independent.
-
##
-
1
module PercentLineEnhancer
-
1
include PrefixedLineEnhancer
-
-
1
def self.desc # :nodoc:
-
"regard lines starting with '%' as program code"
-
end
-
-
#--
-
#def init_generator(properties={})
-
# super
-
# @prefixchar = '%'
-
# @prefixrexp = /^\%(.*?\r?\n)/
-
#end
-
#++
-
-
1
def add_text(src, text)
-
unless @prefixrexp
-
@prefixchar = '%'
-
@prefixrexp = /^\%(.*?\r?\n)/
-
end
-
super(src, text)
-
end
-
-
end
-
-
-
##
-
## [experimental] allow header and footer in eRuby script
-
##
-
## ex.
-
## ====================
-
## ## without header and footer
-
## $ cat ex1.eruby
-
## <% def list_items(list) %>
-
## <% for item in list %>
-
## <li><%= item %></li>
-
## <% end %>
-
## <% end %>
-
##
-
## $ erubis -s ex1.eruby
-
## _buf = []; def list_items(list)
-
## ; for item in list
-
## ; _buf << '<li>'; _buf << ( item ).to_s; _buf << '</li>
-
## '; end
-
## ; end
-
## ;
-
## _buf.join
-
##
-
## ## with header and footer
-
## $ cat ex2.eruby
-
## <!--#header:
-
## def list_items(list)
-
## #-->
-
## <% for item in list %>
-
## <li><%= item %></li>
-
## <% end %>
-
## <!--#footer:
-
## end
-
## #-->
-
##
-
## $ erubis -s -c HeaderFooterEruby ex4.eruby
-
##
-
## def list_items(list)
-
## _buf = []; _buf << '
-
## '; for item in list
-
## ; _buf << '<li>'; _buf << ( item ).to_s; _buf << '</li>
-
## '; end
-
## ; _buf << '
-
## ';
-
## _buf.join
-
## end
-
##
-
## ====================
-
##
-
## this is language-independent.
-
##
-
1
module HeaderFooterEnhancer
-
-
1
def self.desc # :nodoc:
-
"allow header/footer in document (ex. '<!--#header: #-->')"
-
end
-
-
1
HEADER_FOOTER_PATTERN = /(.*?)(^[ \t]*)?<!--\#(\w+):(.*?)\#-->([ \t]*\r?\n)?/m
-
-
1
def add_text(src, text)
-
m = nil
-
text.scan(HEADER_FOOTER_PATTERN) do |txt, lspace, word, content, rspace|
-
m = Regexp.last_match
-
flag_trim = @trim && lspace && rspace
-
super(src, txt)
-
content = "#{lspace}#{content}#{rspace}" if flag_trim
-
super(src, lspace) if !flag_trim && lspace
-
instance_variable_set("@#{word}", content)
-
super(src, rspace) if !flag_trim && rspace
-
end
-
#rest = $' || text # ruby1.8
-
rest = m ? text[m.end(0)..-1] : text # ruby1.9
-
super(src, rest)
-
end
-
-
1
attr_accessor :header, :footer
-
-
1
def convert(input)
-
source = super
-
return @src = "#{@header}#{source}#{@footer}"
-
end
-
-
end
-
-
-
##
-
## delete indentation of HTML.
-
##
-
## this is language-independent.
-
##
-
1
module DeleteIndentEnhancer
-
-
1
def self.desc # :nodoc:
-
"delete indentation of HTML."
-
end
-
-
1
def convert_input(src, input)
-
input = input.gsub(/^[ \t]+</, '<')
-
super(src, input)
-
end
-
-
end
-
-
-
##
-
## convert "<h1><%=title%></h1>" into "_buf << %Q`<h1>#{title}</h1>`"
-
##
-
## this is only for Eruby.
-
##
-
1
module InterpolationEnhancer
-
-
1
def self.desc # :nodoc:
-
"convert '<p><%=text%></p>' into '_buf << %Q`<p>\#{text}</p>`'"
-
end
-
-
1
def convert_input(src, input)
-
pat = @pattern
-
regexp = pat.nil? || pat == '<% %>' ? Basic::Converter::DEFAULT_REGEXP : pattern_regexp(pat)
-
pos = 0
-
is_bol = true # is beginning of line
-
str = ''
-
input.scan(regexp) do |indicator, code, tailch, rspace|
-
match = Regexp.last_match()
-
len = match.begin(0) - pos
-
text = input[pos, len]
-
pos = match.end(0)
-
ch = indicator ? indicator[0] : nil
-
lspace = ch == ?= ? nil : detect_spaces_at_bol(text, is_bol)
-
is_bol = rspace ? true : false
-
_add_text_to_str(str, text)
-
## * when '<%= %>', do nothing
-
## * when '<% %>' or '<%# %>', delete spaces iff only spaces are around '<% %>'
-
if ch == ?= # <%= %>
-
rspace = nil if tailch && !tailch.empty?
-
str << lspace if lspace
-
add_expr(str, code, indicator)
-
str << rspace if rspace
-
elsif ch == ?\# # <%# %>
-
n = code.count("\n") + (rspace ? 1 : 0)
-
if @trim && lspace && rspace
-
add_text(src, str)
-
str = ''
-
add_stmt(src, "\n" * n)
-
else
-
str << lspace if lspace
-
add_text(src, str)
-
str = ''
-
add_stmt(src, "\n" * n)
-
str << rspace if rspace
-
end
-
else # <% %>
-
if @trim && lspace && rspace
-
add_text(src, str)
-
str = ''
-
add_stmt(src, "#{lspace}#{code}#{rspace}")
-
else
-
str << lspace if lspace
-
add_text(src, str)
-
str = ''
-
add_stmt(src, code)
-
str << rspace if rspace
-
end
-
end
-
end
-
#rest = $' || input # ruby1.8
-
rest = pos == 0 ? input : input[pos..-1] # ruby1.9
-
_add_text_to_str(str, rest)
-
add_text(src, str)
-
end
-
-
1
def add_text(src, text)
-
return if !text || text.empty?
-
#src << " _buf << %Q`" << text << "`;"
-
if text[-1] == ?\n
-
text[-1] = "\\n"
-
src << " #{@bufvar} << %Q`#{text}`\n"
-
else
-
src << " #{@bufvar} << %Q`#{text}`;"
-
end
-
end
-
-
1
def _add_text_to_str(str, text)
-
return if !text || text.empty?
-
str << text.gsub(/[`\#\\]/, '\\\\\&')
-
end
-
-
1
def add_expr_escaped(str, code)
-
str << "\#{#{escaped_expr(code)}}"
-
end
-
-
1
def add_expr_literal(str, code)
-
str << "\#{#{code}}"
-
end
-
-
end
-
-
-
end
-
##
-
## $Release: 2.7.0 $
-
## copyright(c) 2006-2011 kuwata-lab.com all rights reserved.
-
##
-
-
1
module Erubis
-
-
-
##
-
## base error class
-
##
-
1
class ErubisError < StandardError
-
end
-
-
-
##
-
## raised when method or function is not supported
-
##
-
1
class NotSupportedError < ErubisError
-
end
-
-
-
end
-
##
-
## $Release: 2.7.0 $
-
## copyright(c) 2006-2011 kuwata-lab.com all rights reserved.
-
##
-
-
1
require 'erubis/error'
-
1
require 'erubis/context'
-
-
-
1
module Erubis
-
-
1
EMPTY_BINDING = binding()
-
-
-
##
-
## evaluate code
-
##
-
1
module Evaluator
-
-
1
def self.supported_properties # :nodoc:
-
return []
-
end
-
-
1
attr_accessor :src, :filename
-
-
1
def init_evaluator(properties)
-
66
@filename = properties[:filename]
-
end
-
-
1
def result(*args)
-
raise NotSupportedError.new("evaluation of code except Ruby is not supported.")
-
end
-
-
1
def evaluate(*args)
-
raise NotSupportedError.new("evaluation of code except Ruby is not supported.")
-
end
-
-
end
-
-
-
##
-
## evaluator for Ruby
-
##
-
1
module RubyEvaluator
-
1
include Evaluator
-
-
1
def self.supported_properties # :nodoc:
-
list = Evaluator.supported_properties
-
return list
-
end
-
-
## eval(@src) with binding object
-
1
def result(_binding_or_hash=TOPLEVEL_BINDING)
-
_arg = _binding_or_hash
-
if _arg.is_a?(Hash)
-
_b = binding()
-
eval _arg.collect{|k,v| "#{k} = _arg[#{k.inspect}]; "}.join, _b
-
elsif _arg.is_a?(Binding)
-
_b = _arg
-
elsif _arg.nil?
-
_b = binding()
-
else
-
raise ArgumentError.new("#{self.class.name}#result(): argument should be Binding or Hash but passed #{_arg.class.name} object.")
-
end
-
return eval(@src, _b, (@filename || '(erubis'))
-
end
-
-
## invoke context.instance_eval(@src)
-
1
def evaluate(_context=Context.new)
-
_context = Context.new(_context) if _context.is_a?(Hash)
-
#return _context.instance_eval(@src, @filename || '(erubis)')
-
#@_proc ||= eval("proc { #{@src} }", Erubis::EMPTY_BINDING, @filename || '(erubis)')
-
@_proc ||= eval("proc { #{@src} }", binding(), @filename || '(erubis)')
-
return _context.instance_eval(&@_proc)
-
end
-
-
## if object is an Class or Module then define instance method to it,
-
## else define singleton method to it.
-
1
def def_method(object, method_name, filename=nil)
-
m = object.is_a?(Module) ? :module_eval : :instance_eval
-
object.__send__(m, "def #{method_name}; #{@src}; end", filename || @filename || '(erubis)')
-
end
-
-
-
end
-
-
-
end
-
##
-
## $Release: 2.7.0 $
-
## copyright(c) 2006-2011 kuwata-lab.com all rights reserved.
-
##
-
-
1
require 'erubis/util'
-
-
1
module Erubis
-
-
-
##
-
## code generator, called by Converter module
-
##
-
1
module Generator
-
-
1
def self.supported_properties() # :nodoc:
-
return [
-
[:escapefunc, nil, "escape function name"],
-
]
-
end
-
-
1
attr_accessor :escapefunc
-
-
1
def init_generator(properties={})
-
66
@escapefunc = properties[:escapefunc]
-
end
-
-
-
## (abstract) escape text string
-
##
-
## ex.
-
## def escape_text(text)
-
## return text.dump
-
## # or return "'" + text.gsub(/['\\]/, '\\\\\&') + "'"
-
## end
-
1
def escape_text(text)
-
not_implemented
-
end
-
-
## return escaped expression code (ex. 'h(...)' or 'htmlspecialchars(...)')
-
1
def escaped_expr(code)
-
code.strip!
-
return "#{@escapefunc}(#{code})"
-
end
-
-
## (abstract) add @preamble to src
-
1
def add_preamble(src)
-
not_implemented
-
end
-
-
## (abstract) add text string to src
-
1
def add_text(src, text)
-
not_implemented
-
end
-
-
## (abstract) add statement code to src
-
1
def add_stmt(src, code)
-
not_implemented
-
end
-
-
## (abstract) add expression literal code to src. this is called by add_expr().
-
1
def add_expr_literal(src, code)
-
not_implemented
-
end
-
-
## (abstract) add escaped expression code to src. this is called by add_expr().
-
1
def add_expr_escaped(src, code)
-
not_implemented
-
end
-
-
## (abstract) add expression code to src for debug. this is called by add_expr().
-
1
def add_expr_debug(src, code)
-
not_implemented
-
end
-
-
## (abstract) add @postamble to src
-
1
def add_postamble(src)
-
not_implemented
-
end
-
-
-
end
-
-
-
end
-
##
-
## $Release: 2.7.0 $
-
## copyright(c) 2006-2011 kuwata-lab.com all rights reserved.
-
##
-
-
-
1
module Erubis
-
-
##
-
## helper for xml
-
##
-
1
module XmlHelper
-
-
1
module_function
-
-
1
ESCAPE_TABLE = {
-
'&' => '&',
-
'<' => '<',
-
'>' => '>',
-
'"' => '"',
-
"'" => ''',
-
}
-
-
1
def escape_xml(value)
-
value.to_s.gsub(/[&<>"]/) { |s| ESCAPE_TABLE[s] } # or /[&<>"']/
-
#value.to_s.gsub(/[&<>"]/) { ESCAPE_TABLE[$&] }
-
end
-
-
1
def escape_xml2(value)
-
return value.to_s.gsub(/\&/,'&').gsub(/</,'<').gsub(/>/,'>').gsub(/"/,'"')
-
end
-
-
1
alias h escape_xml
-
1
alias html_escape escape_xml
-
-
1
def url_encode(str)
-
return str.gsub(/[^-_.a-zA-Z0-9]+/) { |s|
-
s.unpack('C*').collect { |i| "%%%02X" % i }.join
-
}
-
end
-
-
1
alias u url_encode
-
-
end
-
-
-
end
-
##
-
## $Release: 2.7.0 $
-
## copyright(c) 2006-2011 kuwata-lab.com all rights reserved.
-
##
-
-
##
-
## you can add site-local settings here.
-
## this files is required by erubis.rb
-
##
-
##
-
## $Release: 2.7.0 $
-
## copyright(c) 2006-2011 kuwata-lab.com all rights reserved.
-
##
-
-
1
module Kernel
-
-
##
-
## raise NotImplementedError
-
##
-
1
def not_implemented #:doc:
-
backtrace = caller()
-
method_name = (backtrace.shift =~ /`(\w+)'$/) && $1
-
mesg = "class #{self.class.name} must implement abstract method '#{method_name}()'."
-
#mesg = "#{self.class.name}##{method_name}() is not implemented."
-
err = NotImplementedError.new mesg
-
err.set_backtrace backtrace
-
raise err
-
end
-
1
private :not_implemented
-
-
end
-
1
require 'i18n/version'
-
1
require 'i18n/exceptions'
-
1
require 'i18n/interpolate/ruby'
-
-
1
module I18n
-
1
autoload :Backend, 'i18n/backend'
-
1
autoload :Config, 'i18n/config'
-
1
autoload :Gettext, 'i18n/gettext'
-
1
autoload :Locale, 'i18n/locale'
-
1
autoload :Tests, 'i18n/tests'
-
-
1
RESERVED_KEYS = [:scope, :default, :separator, :resolve, :object, :fallback, :format, :cascade, :throw, :raise, :rescue_format]
-
1
RESERVED_KEYS_PATTERN = /%\{(#{RESERVED_KEYS.join("|")})\}/
-
-
extend Module.new {
-
# Gets I18n configuration object.
-
1
def config
-
831
Thread.current[:i18n_config] ||= I18n::Config.new
-
end
-
-
# Sets I18n configuration object.
-
1
def config=(value)
-
242
Thread.current[:i18n_config] = value
-
end
-
-
# Write methods which delegates to the configuration object
-
1
%w(locale backend default_locale available_locales default_separator
-
exception_handler load_path).each do |method|
-
7
module_eval <<-DELEGATORS, __FILE__, __LINE__ + 1
-
def #{method}
-
config.#{method}
-
end
-
-
def #{method}=(value)
-
config.#{method} = (value)
-
end
-
DELEGATORS
-
end
-
-
# Tells the backend to reload translations. Used in situations like the
-
# Rails development environment. Backends can implement whatever strategy
-
# is useful.
-
1
def reload!
-
config.backend.reload!
-
end
-
-
# Translates, pluralizes and interpolates a given key using a given locale,
-
# scope, and default, as well as interpolation values.
-
#
-
# *LOOKUP*
-
#
-
# Translation data is organized as a nested hash using the upper-level keys
-
# as namespaces. <em>E.g.</em>, ActionView ships with the translation:
-
# <tt>:date => {:formats => {:short => "%b %d"}}</tt>.
-
#
-
# Translations can be looked up at any level of this hash using the key argument
-
# and the scope option. <em>E.g.</em>, in this example <tt>I18n.t :date</tt>
-
# returns the whole translations hash <tt>{:formats => {:short => "%b %d"}}</tt>.
-
#
-
# Key can be either a single key or a dot-separated key (both Strings and Symbols
-
# work). <em>E.g.</em>, the short format can be looked up using both:
-
# I18n.t 'date.formats.short'
-
# I18n.t :'date.formats.short'
-
#
-
# Scope can be either a single key, a dot-separated key or an array of keys
-
# or dot-separated keys. Keys and scopes can be combined freely. So these
-
# examples will all look up the same short date format:
-
# I18n.t 'date.formats.short'
-
# I18n.t 'formats.short', :scope => 'date'
-
# I18n.t 'short', :scope => 'date.formats'
-
# I18n.t 'short', :scope => %w(date formats)
-
#
-
# *INTERPOLATION*
-
#
-
# Translations can contain interpolation variables which will be replaced by
-
# values passed to #translate as part of the options hash, with the keys matching
-
# the interpolation variable names.
-
#
-
# <em>E.g.</em>, with a translation <tt>:foo => "foo %{bar}"</tt> the option
-
# value for the key +bar+ will be interpolated into the translation:
-
# I18n.t :foo, :bar => 'baz' # => 'foo baz'
-
#
-
# *PLURALIZATION*
-
#
-
# Translation data can contain pluralized translations. Pluralized translations
-
# are arrays of singluar/plural versions of translations like <tt>['Foo', 'Foos']</tt>.
-
#
-
# Note that <tt>I18n::Backend::Simple</tt> only supports an algorithm for English
-
# pluralization rules. Other algorithms can be supported by custom backends.
-
#
-
# This returns the singular version of a pluralized translation:
-
# I18n.t :foo, :count => 1 # => 'Foo'
-
#
-
# These both return the plural version of a pluralized translation:
-
# I18n.t :foo, :count => 0 # => 'Foos'
-
# I18n.t :foo, :count => 2 # => 'Foos'
-
#
-
# The <tt>:count</tt> option can be used both for pluralization and interpolation.
-
# <em>E.g.</em>, with the translation
-
# <tt>:foo => ['%{count} foo', '%{count} foos']</tt>, count will
-
# be interpolated to the pluralized translation:
-
# I18n.t :foo, :count => 1 # => '1 foo'
-
#
-
# *DEFAULTS*
-
#
-
# This returns the translation for <tt>:foo</tt> or <tt>default</tt> if no translation was found:
-
# I18n.t :foo, :default => 'default'
-
#
-
# This returns the translation for <tt>:foo</tt> or the translation for <tt>:bar</tt> if no
-
# translation for <tt>:foo</tt> was found:
-
# I18n.t :foo, :default => :bar
-
#
-
# Returns the translation for <tt>:foo</tt> or the translation for <tt>:bar</tt>
-
# or <tt>default</tt> if no translations for <tt>:foo</tt> and <tt>:bar</tt> were found.
-
# I18n.t :foo, :default => [:bar, 'default']
-
#
-
# *BULK LOOKUP*
-
#
-
# This returns an array with the translations for <tt>:foo</tt> and <tt>:bar</tt>.
-
# I18n.t [:foo, :bar]
-
#
-
# Can be used with dot-separated nested keys:
-
# I18n.t [:'baz.foo', :'baz.bar']
-
#
-
# Which is the same as using a scope option:
-
# I18n.t [:foo, :bar], :scope => :baz
-
#
-
# *LAMBDAS*
-
#
-
# Both translations and defaults can be given as Ruby lambdas. Lambdas will be
-
# called and passed the key and options.
-
#
-
# E.g. assuming the key <tt>:salutation</tt> resolves to:
-
# lambda { |key, options| options[:gender] == 'm' ? "Mr. %{options[:name]}" : "Mrs. %{options[:name]}" }
-
#
-
# Then <tt>I18n.t(:salutation, :gender => 'w', :name => 'Smith') will result in "Mrs. Smith".
-
#
-
# It is recommended to use/implement lambdas in an "idempotent" way. E.g. when
-
# a cache layer is put in front of I18n.translate it will generate a cache key
-
# from the argument values passed to #translate. Therefor your lambdas should
-
# always return the same translations/values per unique combination of argument
-
# values.
-
1
def translate(*args)
-
71
options = args.last.is_a?(Hash) ? args.pop : {}
-
71
key = args.shift
-
71
backend = config.backend
-
71
locale = options.delete(:locale) || config.locale
-
71
handling = options.delete(:throw) && :throw || options.delete(:raise) && :raise # TODO deprecate :raise
-
-
71
raise I18n::ArgumentError if key.is_a?(String) && key.empty?
-
-
71
result = catch(:exception) do
-
71
if key.is_a?(Array)
-
key.map { |k| backend.translate(locale, k, options) }
-
else
-
71
backend.translate(locale, key, options)
-
end
-
end
-
71
result.is_a?(MissingTranslation) ? handle_exception(handling, result, locale, key, options) : result
-
end
-
1
alias :t :translate
-
-
# Wrapper for <tt>translate</tt> that adds <tt>:raise => true</tt>. With
-
# this option, if no translation is found, it will raise <tt>I18n::MissingTranslationData</tt>
-
1
def translate!(key, options={})
-
translate(key, options.merge(:raise => true))
-
end
-
1
alias :t! :translate!
-
-
# Transliterates UTF-8 characters to ASCII. By default this method will
-
# transliterate only Latin strings to an ASCII approximation:
-
#
-
# I18n.transliterate("Ærøskøbing")
-
# # => "AEroskobing"
-
#
-
# I18n.transliterate("日本語")
-
# # => "???"
-
#
-
# It's also possible to add support for per-locale transliterations. I18n
-
# expects transliteration rules to be stored at
-
# <tt>i18n.transliterate.rule</tt>.
-
#
-
# Transliteration rules can either be a Hash or a Proc. Procs must accept a
-
# single string argument. Hash rules inherit the default transliteration
-
# rules, while Procs do not.
-
#
-
# *Examples*
-
#
-
# Setting a Hash in <locale>.yml:
-
#
-
# i18n:
-
# transliterate:
-
# rule:
-
# ü: "ue"
-
# ö: "oe"
-
#
-
# Setting a Hash using Ruby:
-
#
-
# store_translations(:de, :i18n => {
-
# :transliterate => {
-
# :rule => {
-
# "ü" => "ue",
-
# "ö" => "oe"
-
# }
-
# }
-
# )
-
#
-
# Setting a Proc:
-
#
-
# translit = lambda {|string| MyTransliterator.transliterate(string) }
-
# store_translations(:xx, :i18n => {:transliterate => {:rule => translit})
-
#
-
# Transliterating strings:
-
#
-
# I18n.locale = :en
-
# I18n.transliterate("Jürgen") # => "Jurgen"
-
# I18n.locale = :de
-
# I18n.transliterate("Jürgen") # => "Juergen"
-
# I18n.transliterate("Jürgen", :locale => :en) # => "Jurgen"
-
# I18n.transliterate("Jürgen", :locale => :de) # => "Juergen"
-
1
def transliterate(*args)
-
options = args.pop if args.last.is_a?(Hash)
-
key = args.shift
-
locale = options && options.delete(:locale) || config.locale
-
handling = options && (options.delete(:throw) && :throw || options.delete(:raise) && :raise)
-
replacement = options && options.delete(:replacement)
-
config.backend.transliterate(locale, key, replacement)
-
rescue I18n::ArgumentError => exception
-
handle_exception(handling, exception, locale, key, options || {})
-
end
-
-
# Localizes certain objects, such as dates and numbers to local formatting.
-
1
def localize(object, options = {})
-
locale = options.delete(:locale) || config.locale
-
format = options.delete(:format) || :default
-
config.backend.localize(locale, object, format, options)
-
end
-
1
alias :l :localize
-
-
# Executes block with given I18n.locale set.
-
1
def with_locale(tmp_locale = nil)
-
if tmp_locale
-
current_locale = self.locale
-
self.locale = tmp_locale
-
end
-
yield
-
ensure
-
self.locale = current_locale if tmp_locale
-
end
-
-
# Merges the given locale, key and scope into a single array of keys.
-
# Splits keys that contain dots into multiple keys. Makes sure all
-
# keys are Symbols.
-
1
def normalize_keys(locale, key, scope, separator = nil)
-
71
separator ||= I18n.default_separator
-
-
71
keys = []
-
71
keys.concat normalize_key(locale, separator)
-
71
keys.concat normalize_key(scope, separator)
-
71
keys.concat normalize_key(key, separator)
-
71
keys
-
end
-
-
# making these private until Ruby 1.9.2 can send to protected methods again
-
# see http://redmine.ruby-lang.org/repositories/revision/ruby-19?rev=24280
-
1
private
-
-
# Any exceptions thrown in translate will be sent to the @@exception_handler
-
# which can be a Symbol, a Proc or any other Object unless they're forced to
-
# be raised or thrown (MissingTranslation).
-
#
-
# If exception_handler is a Symbol then it will simply be sent to I18n as
-
# a method call. A Proc will simply be called. In any other case the
-
# method #call will be called on the exception_handler object.
-
#
-
# Examples:
-
#
-
# I18n.exception_handler = :default_exception_handler # this is the default
-
# I18n.default_exception_handler(exception, locale, key, options) # will be called like this
-
#
-
# I18n.exception_handler = lambda { |*args| ... } # a lambda
-
# I18n.exception_handler.call(exception, locale, key, options) # will be called like this
-
#
-
# I18n.exception_handler = I18nExceptionHandler.new # an object
-
# I18n.exception_handler.call(exception, locale, key, options) # will be called like this
-
1
def handle_exception(handling, exception, locale, key, options)
-
case handling
-
when :raise
-
raise(exception.respond_to?(:to_exception) ? exception.to_exception : exception)
-
when :throw
-
throw :exception, exception
-
else
-
case handler = options[:exception_handler] || config.exception_handler
-
when Symbol
-
send(handler, exception, locale, key, options)
-
else
-
handler.call(exception, locale, key, options)
-
end
-
end
-
end
-
-
1
def normalize_key(key, separator)
-
267
normalized_key_cache[separator][key] ||=
-
case key
-
when Array
-
81
key.map { |k| normalize_key(k, separator) }.flatten
-
else
-
36
keys = key.to_s.split(separator)
-
36
keys.delete('')
-
77
keys.map! { |k| k.to_sym }
-
36
keys
-
end
-
end
-
-
1
def normalized_key_cache
-
268
@normalized_key_cache ||= Hash.new { |h,k| h[k] = {} }
-
end
-
-
# DEPRECATED. Use I18n.normalize_keys instead.
-
1
def normalize_translation_keys(locale, key, scope, separator = nil)
-
puts "I18n.normalize_translation_keys is deprecated. Please use the class I18n.normalize_keys instead."
-
normalize_keys(locale, key, scope, separator)
-
end
-
-
# DEPRECATED. Please use the I18n::ExceptionHandler class instead.
-
1
def default_exception_handler(exception, locale, key, options)
-
puts "I18n.default_exception_handler is deprecated. Please use the class I18n::ExceptionHandler instead " +
-
"(an instance of which is set to I18n.exception_handler by default)."
-
exception.is_a?(MissingTranslation) ? exception.message : raise(exception)
-
end
-
1
}
-
end
-
1
module I18n
-
1
module Backend
-
1
autoload :Base, 'i18n/backend/base'
-
1
autoload :InterpolationCompiler, 'i18n/backend/interpolation_compiler'
-
1
autoload :Cache, 'i18n/backend/cache'
-
1
autoload :Cascade, 'i18n/backend/cascade'
-
1
autoload :Chain, 'i18n/backend/chain'
-
1
autoload :Fallbacks, 'i18n/backend/fallbacks'
-
1
autoload :Flatten, 'i18n/backend/flatten'
-
1
autoload :Gettext, 'i18n/backend/gettext'
-
1
autoload :KeyValue, 'i18n/backend/key_value'
-
1
autoload :Memoize, 'i18n/backend/memoize'
-
1
autoload :Metadata, 'i18n/backend/metadata'
-
1
autoload :Pluralization, 'i18n/backend/pluralization'
-
1
autoload :Simple, 'i18n/backend/simple'
-
1
autoload :Transliterator, 'i18n/backend/transliterator'
-
end
-
end
-
1
require 'yaml'
-
1
require 'i18n/core_ext/hash'
-
1
require 'i18n/core_ext/kernel/surpress_warnings'
-
-
1
module I18n
-
1
module Backend
-
1
module Base
-
1
include I18n::Backend::Transliterator
-
-
# Accepts a list of paths to translation files. Loads translations from
-
# plain Ruby (*.rb) or YAML files (*.yml). See #load_rb and #load_yml
-
# for details.
-
1
def load_translations(*filenames)
-
1
filenames = I18n.load_path if filenames.empty?
-
3
filenames.flatten.each { |filename| load_file(filename) }
-
end
-
-
# This method receives a locale, a data hash and options for storing translations.
-
# Should be implemented
-
1
def store_translations(locale, data, options = {})
-
raise NotImplementedError
-
end
-
-
1
def translate(locale, key, options = {})
-
71
raise InvalidLocale.new(locale) unless locale
-
71
entry = key && lookup(locale, key, options[:scope], options)
-
-
71
if options.empty?
-
1
entry = resolve(locale, key, entry, options)
-
else
-
70
count, default = options.values_at(:count, :default)
-
70
values = options.except(*RESERVED_KEYS)
-
70
entry = entry.nil? && default ?
-
default(locale, key, default, options) : resolve(locale, key, entry, options)
-
end
-
-
71
throw(:exception, I18n::MissingTranslation.new(locale, key, options)) if entry.nil?
-
71
entry = entry.dup if entry.is_a?(String)
-
-
71
entry = pluralize(locale, entry, count) if count
-
71
entry = interpolate(locale, entry, values) if values
-
71
entry
-
end
-
-
# Acts the same as +strftime+, but uses a localized version of the
-
# format string. Takes a key from the date/time formats translations as
-
# a format argument (<em>e.g.</em>, <tt>:short</tt> in <tt>:'date.formats'</tt>).
-
1
def localize(locale, object, format = :default, options = {})
-
raise ArgumentError, "Object must be a Date, DateTime or Time object. #{object.inspect} given." unless object.respond_to?(:strftime)
-
-
if Symbol === format
-
key = format
-
type = object.respond_to?(:sec) ? 'time' : 'date'
-
options = options.merge(:raise => true, :object => object, :locale => locale)
-
format = I18n.t(:"#{type}.formats.#{key}", options)
-
end
-
-
# format = resolve(locale, object, format, options)
-
format = format.to_s.gsub(/%[aAbBp]/) do |match|
-
case match
-
when '%a' then I18n.t(:"date.abbr_day_names", :locale => locale, :format => format)[object.wday]
-
when '%A' then I18n.t(:"date.day_names", :locale => locale, :format => format)[object.wday]
-
when '%b' then I18n.t(:"date.abbr_month_names", :locale => locale, :format => format)[object.mon]
-
when '%B' then I18n.t(:"date.month_names", :locale => locale, :format => format)[object.mon]
-
when '%p' then I18n.t(:"time.#{object.hour < 12 ? :am : :pm}", :locale => locale, :format => format) if object.respond_to? :hour
-
end
-
end
-
-
object.strftime(format)
-
end
-
-
# Returns an array of locales for which translations are available
-
# ignoring the reserved translation meta data key :i18n.
-
1
def available_locales
-
raise NotImplementedError
-
end
-
-
1
def reload!
-
@skip_syntax_deprecation = false
-
end
-
-
1
protected
-
-
# The method which actually looks up for the translation in the store.
-
1
def lookup(locale, key, scope = [], options = {})
-
raise NotImplementedError
-
end
-
-
# Evaluates defaults.
-
# If given subject is an Array, it walks the array and returns the
-
# first translation that can be resolved. Otherwise it tries to resolve
-
# the translation directly.
-
1
def default(locale, object, subject, options = {})
-
204
options = options.dup.reject { |key, value| key == :default }
-
68
case subject
-
when Array
-
subject.each do |item|
-
result = resolve(locale, object, item, options) and return result
-
end and nil
-
else
-
68
resolve(locale, object, subject, options)
-
end
-
end
-
-
# Resolves a translation.
-
# If the given subject is a Symbol, it will be translated with the
-
# given options. If it is a Proc then it will be evaluated. All other
-
# subjects will be returned directly.
-
1
def resolve(locale, object, subject, options = {})
-
71
return subject if options[:resolve] == false
-
71
result = catch(:exception) do
-
71
case subject
-
when Symbol
-
I18n.translate(subject, options.merge(:locale => locale, :throw => true))
-
when Proc
-
date_or_time = options.delete(:object) || object
-
resolve(locale, object, subject.call(date_or_time, options))
-
else
-
71
subject
-
end
-
end
-
71
result unless result.is_a?(MissingTranslation)
-
end
-
-
# Picks a translation from an array according to English pluralization
-
# rules. It will pick the first translation if count is not equal to 1
-
# and the second translation if it is equal to 1. Other backends can
-
# implement more flexible or complex pluralization rules.
-
1
def pluralize(locale, entry, count)
-
return entry unless entry.is_a?(Hash) && count
-
-
key = :zero if count == 0 && entry.has_key?(:zero)
-
key ||= count == 1 ? :one : :other
-
raise InvalidPluralizationData.new(entry, count) unless entry.has_key?(key)
-
entry[key]
-
end
-
-
# Interpolates values into a given string.
-
#
-
# interpolate "file %{file} opened by %%{user}", :file => 'test.txt', :user => 'Mr. X'
-
# # => "file test.txt opened by %{user}"
-
1
def interpolate(locale, string, values = {})
-
70
if string.is_a?(::String) && !values.empty?
-
1
I18n.interpolate(string, values)
-
else
-
69
string
-
end
-
end
-
-
# Loads a single translations file by delegating to #load_rb or
-
# #load_yml depending on the file extension and directly merges the
-
# data to the existing translations. Raises I18n::UnknownFileType
-
# for all other file extensions.
-
1
def load_file(filename)
-
2
type = File.extname(filename).tr('.', '').downcase
-
2
raise UnknownFileType.new(type, filename) unless respond_to?(:"load_#{type}", true)
-
2
data = send(:"load_#{type}", filename)
-
2
raise InvalidLocaleData.new(filename) unless data.is_a?(Hash)
-
4
data.each { |locale, d| store_translations(locale, d || {}) }
-
end
-
-
# Loads a plain Ruby translations file. eval'ing the file must yield
-
# a Hash containing translation data with locales as toplevel keys.
-
1
def load_rb(filename)
-
eval(IO.read(filename), binding, filename)
-
end
-
-
# Loads a YAML translations file. The data must have locales as
-
# toplevel keys.
-
1
def load_yml(filename)
-
2
begin
-
2
YAML.load_file(filename)
-
rescue TypeError
-
nil
-
rescue SyntaxError
-
nil
-
end
-
end
-
end
-
end
-
end
-
1
module I18n
-
1
module Backend
-
# A simple backend that reads translations from YAML files and stores them in
-
# an in-memory hash. Relies on the Base backend.
-
#
-
# The implementation is provided by a Implementation module allowing to easily
-
# extend Simple backend's behavior by including modules. E.g.:
-
#
-
# module I18n::Backend::Pluralization
-
# def pluralize(*args)
-
# # extended pluralization logic
-
# super
-
# end
-
# end
-
#
-
# I18n::Backend::Simple.include(I18n::Backend::Pluralization)
-
1
class Simple
-
3
(class << self; self; end).class_eval { public :include }
-
-
1
module Implementation
-
1
include Base
-
-
1
def initialized?
-
71
@initialized ||= false
-
end
-
-
# Stores translations for the given locale in memory.
-
# This uses a deep merge for the translations hash, so existing
-
# translations will be overwritten by new ones only at the deepest
-
# level of the hash.
-
1
def store_translations(locale, data, options = {})
-
5
locale = locale.to_sym
-
5
translations[locale] ||= {}
-
5
data = data.deep_symbolize_keys
-
5
translations[locale].deep_merge!(data)
-
end
-
-
# Get available locales from the translations hash
-
1
def available_locales
-
init_translations unless initialized?
-
translations.inject([]) do |locales, (locale, data)|
-
locales << locale unless (data.keys - [:i18n]).empty?
-
locales
-
end
-
end
-
-
# Clean up translations hash and set initialized to false on reload!
-
1
def reload!
-
@initialized = false
-
@translations = nil
-
super
-
end
-
-
1
protected
-
-
1
def init_translations
-
1
load_translations
-
1
@initialized = true
-
end
-
-
1
def translations
-
81
@translations ||= {}
-
end
-
-
# Looks up a translation from the translations hash. Returns nil if
-
# eiher key is nil, or locale, scope or key do not exist as a key in the
-
# nested translations hash. Splits keys or scopes containing dots
-
# into multiple keys, i.e. <tt>currency.format</tt> is regarded the same as
-
# <tt>%w(currency format)</tt>.
-
1
def lookup(locale, key, scope = [], options = {})
-
71
init_translations unless initialized?
-
71
keys = I18n.normalize_keys(locale, key, scope, options[:separator])
-
-
71
keys.inject(translations) do |result, _key|
-
169
_key = _key.to_sym
-
169
return nil unless result.is_a?(Hash) && result.has_key?(_key)
-
101
result = result[_key]
-
101
result = resolve(locale, _key, result, options.merge(:scope => nil)) if result.is_a?(Symbol)
-
101
result
-
end
-
end
-
end
-
-
1
include Implementation
-
end
-
end
-
end
-
# encoding: utf-8
-
1
module I18n
-
1
module Backend
-
1
module Transliterator
-
1
DEFAULT_REPLACEMENT_CHAR = "?"
-
-
# Given a locale and a UTF-8 string, return the locale's ASCII
-
# approximation for the string.
-
1
def transliterate(locale, string, replacement = nil)
-
@transliterators ||= {}
-
@transliterators[locale] ||= Transliterator.get I18n.t(:'i18n.transliterate.rule',
-
:locale => locale, :resolve => false, :default => {})
-
@transliterators[locale].transliterate(string, replacement)
-
end
-
-
# Get a transliterator instance.
-
1
def self.get(rule = nil)
-
if !rule || rule.kind_of?(Hash)
-
HashTransliterator.new(rule)
-
elsif rule.kind_of? Proc
-
ProcTransliterator.new(rule)
-
else
-
raise I18n::ArgumentError, "Transliteration rule must be a proc or a hash."
-
end
-
end
-
-
# A transliterator which accepts a Proc as its transliteration rule.
-
1
class ProcTransliterator
-
1
def initialize(rule)
-
@rule = rule
-
end
-
-
1
def transliterate(string, replacement = nil)
-
@rule.call(string)
-
end
-
end
-
-
# A transliterator which accepts a Hash of characters as its translation
-
# rule.
-
1
class HashTransliterator
-
1
DEFAULT_APPROXIMATIONS = {
-
"À"=>"A", "Á"=>"A", "Â"=>"A", "Ã"=>"A", "Ä"=>"A", "Å"=>"A", "Æ"=>"AE",
-
"Ç"=>"C", "È"=>"E", "É"=>"E", "Ê"=>"E", "Ë"=>"E", "Ì"=>"I", "Í"=>"I",
-
"Î"=>"I", "Ï"=>"I", "Ð"=>"D", "Ñ"=>"N", "Ò"=>"O", "Ó"=>"O", "Ô"=>"O",
-
"Õ"=>"O", "Ö"=>"O", "×"=>"x", "Ø"=>"O", "Ù"=>"U", "Ú"=>"U", "Û"=>"U",
-
"Ü"=>"U", "Ý"=>"Y", "Þ"=>"Th", "ß"=>"ss", "à"=>"a", "á"=>"a", "â"=>"a",
-
"ã"=>"a", "ä"=>"a", "å"=>"a", "æ"=>"ae", "ç"=>"c", "è"=>"e", "é"=>"e",
-
"ê"=>"e", "ë"=>"e", "ì"=>"i", "í"=>"i", "î"=>"i", "ï"=>"i", "ð"=>"d",
-
"ñ"=>"n", "ò"=>"o", "ó"=>"o", "ô"=>"o", "õ"=>"o", "ö"=>"o", "ø"=>"o",
-
"ù"=>"u", "ú"=>"u", "û"=>"u", "ü"=>"u", "ý"=>"y", "þ"=>"th", "ÿ"=>"y",
-
"Ā"=>"A", "ā"=>"a", "Ă"=>"A", "ă"=>"a", "Ą"=>"A", "ą"=>"a", "Ć"=>"C",
-
"ć"=>"c", "Ĉ"=>"C", "ĉ"=>"c", "Ċ"=>"C", "ċ"=>"c", "Č"=>"C", "č"=>"c",
-
"Ď"=>"D", "ď"=>"d", "Đ"=>"D", "đ"=>"d", "Ē"=>"E", "ē"=>"e", "Ĕ"=>"E",
-
"ĕ"=>"e", "Ė"=>"E", "ė"=>"e", "Ę"=>"E", "ę"=>"e", "Ě"=>"E", "ě"=>"e",
-
"Ĝ"=>"G", "ĝ"=>"g", "Ğ"=>"G", "ğ"=>"g", "Ġ"=>"G", "ġ"=>"g", "Ģ"=>"G",
-
"ģ"=>"g", "Ĥ"=>"H", "ĥ"=>"h", "Ħ"=>"H", "ħ"=>"h", "Ĩ"=>"I", "ĩ"=>"i",
-
"Ī"=>"I", "ī"=>"i", "Ĭ"=>"I", "ĭ"=>"i", "Į"=>"I", "į"=>"i", "İ"=>"I",
-
"ı"=>"i", "IJ"=>"IJ", "ij"=>"ij", "Ĵ"=>"J", "ĵ"=>"j", "Ķ"=>"K", "ķ"=>"k",
-
"ĸ"=>"k", "Ĺ"=>"L", "ĺ"=>"l", "Ļ"=>"L", "ļ"=>"l", "Ľ"=>"L", "ľ"=>"l",
-
"Ŀ"=>"L", "ŀ"=>"l", "Ł"=>"L", "ł"=>"l", "Ń"=>"N", "ń"=>"n", "Ņ"=>"N",
-
"ņ"=>"n", "Ň"=>"N", "ň"=>"n", "ʼn"=>"'n", "Ŋ"=>"NG", "ŋ"=>"ng",
-
"Ō"=>"O", "ō"=>"o", "Ŏ"=>"O", "ŏ"=>"o", "Ő"=>"O", "ő"=>"o", "Œ"=>"OE",
-
"œ"=>"oe", "Ŕ"=>"R", "ŕ"=>"r", "Ŗ"=>"R", "ŗ"=>"r", "Ř"=>"R", "ř"=>"r",
-
"Ś"=>"S", "ś"=>"s", "Ŝ"=>"S", "ŝ"=>"s", "Ş"=>"S", "ş"=>"s", "Š"=>"S",
-
"š"=>"s", "Ţ"=>"T", "ţ"=>"t", "Ť"=>"T", "ť"=>"t", "Ŧ"=>"T", "ŧ"=>"t",
-
"Ũ"=>"U", "ũ"=>"u", "Ū"=>"U", "ū"=>"u", "Ŭ"=>"U", "ŭ"=>"u", "Ů"=>"U",
-
"ů"=>"u", "Ű"=>"U", "ű"=>"u", "Ų"=>"U", "ų"=>"u", "Ŵ"=>"W", "ŵ"=>"w",
-
"Ŷ"=>"Y", "ŷ"=>"y", "Ÿ"=>"Y", "Ź"=>"Z", "ź"=>"z", "Ż"=>"Z", "ż"=>"z",
-
"Ž"=>"Z", "ž"=>"z"
-
}
-
-
1
def initialize(rule = nil)
-
@rule = rule
-
add DEFAULT_APPROXIMATIONS
-
add rule if rule
-
end
-
-
1
def transliterate(string, replacement = nil)
-
string.gsub(/[^\x00-\x7f]/u) do |char|
-
approximations[char] || replacement || DEFAULT_REPLACEMENT_CHAR
-
end
-
end
-
-
1
private
-
-
1
def approximations
-
@approximations ||= {}
-
end
-
-
# Add transliteration rules to the approximations hash.
-
1
def add(hash)
-
hash.keys.each {|key| hash[key.to_s] = hash.delete(key).to_s}
-
approximations.merge! hash
-
end
-
end
-
end
-
end
-
end
-
1
module I18n
-
1
class Config
-
# The only configuration value that is not global and scoped to thread is :locale.
-
# It defaults to the default_locale.
-
1
def locale
-
314
@locale ||= default_locale
-
end
-
-
# Sets the current locale pseudo-globally, i.e. in the Thread.current hash.
-
1
def locale=(locale)
-
4
@locale = locale.to_sym rescue nil
-
end
-
-
# Returns the current backend. Defaults to +Backend::Simple+.
-
1
def backend
-
74
@@backend ||= Backend::Simple.new
-
end
-
-
# Sets the current backend. Used to set a custom backend.
-
1
def backend=(backend)
-
@@backend = backend
-
end
-
-
# Returns the current default locale. Defaults to :'en'
-
1
def default_locale
-
122
@@default_locale ||= :en
-
end
-
-
# Sets the current default locale. Used to set a custom default locale.
-
1
def default_locale=(locale)
-
@@default_locale = locale.to_sym rescue nil
-
end
-
-
# Returns an array of locales for which translations are available.
-
# Unless you explicitely set these through I18n.available_locales=
-
# the call will be delegated to the backend.
-
1
def available_locales
-
@@available_locales ||= nil
-
@@available_locales || backend.available_locales
-
end
-
-
# Sets the available locales.
-
1
def available_locales=(locales)
-
@@available_locales = Array(locales).map { |locale| locale.to_sym }
-
@@available_locales = nil if @@available_locales.empty?
-
end
-
-
# Returns the current default scope separator. Defaults to '.'
-
1
def default_separator
-
71
@@default_separator ||= '.'
-
end
-
-
# Sets the current default scope separator.
-
1
def default_separator=(separator)
-
@@default_separator = separator
-
end
-
-
# Return the current exception handler. Defaults to :default_exception_handler.
-
1
def exception_handler
-
@@exception_handler ||= ExceptionHandler.new
-
end
-
-
# Sets the exception handler.
-
1
def exception_handler=(exception_handler)
-
@@exception_handler = exception_handler
-
end
-
-
# Allow clients to register paths providing translation data sources. The
-
# backend defines acceptable sources.
-
#
-
# E.g. the provided SimpleBackend accepts a list of paths to translation
-
# files which are either named *.rb and contain plain Ruby Hashes or are
-
# named *.yml and contain YAML data. So for the SimpleBackend clients may
-
# register translation files like this:
-
# I18n.load_path << 'path/to/locale/en.yml'
-
1
def load_path
-
3
@@load_path ||= []
-
end
-
-
# Sets the load path instance. Custom implementations are expected to
-
# behave like a Ruby Array.
-
1
def load_path=(load_path)
-
@@load_path = load_path
-
end
-
end
-
end
-
1
class Hash
-
def slice(*keep_keys)
-
h = {}
-
keep_keys.each { |key| h[key] = fetch(key) }
-
h
-
1
end unless Hash.method_defined?(:slice)
-
-
def except(*less_keys)
-
slice(*keys - less_keys)
-
1
end unless Hash.method_defined?(:except)
-
-
def deep_symbolize_keys
-
inject({}) { |result, (key, value)|
-
value = value.deep_symbolize_keys if value.is_a?(Hash)
-
result[(key.to_sym rescue key) || key] = value
-
result
-
}
-
1
end unless Hash.method_defined?(:deep_symbolize_keys)
-
-
# deep_merge_hash! by Stefan Rusterholz, see http://www.ruby-forum.com/topic/142809
-
1
MERGER = proc do |key, v1, v2|
-
Hash === v1 && Hash === v2 ? v1.merge(v2, &MERGER) : v2
-
end
-
-
def deep_merge!(data)
-
merge!(data, &MERGER)
-
1
end unless Hash.method_defined?(:deep_merge!)
-
end
-
-
1
module Kernel
-
1
def suppress_warnings
-
original_verbosity = $VERBOSE
-
$VERBOSE = nil
-
result = yield
-
$VERBOSE = original_verbosity
-
result
-
end
-
end
-
1
module I18n
-
# Handles exceptions raised in the backend. All exceptions except for
-
# MissingTranslationData exceptions are re-thrown. When a MissingTranslationData
-
# was caught the handler returns an error message string containing the key/scope.
-
# Note that the exception handler is not called when the option :throw was given.
-
1
class ExceptionHandler
-
include Module.new {
-
1
def call(exception, locale, key, options)
-
if exception.is_a?(MissingTranslation)
-
options[:rescue_format] == :html ? exception.html_message : exception.message
-
elsif exception.is_a?(Exception)
-
raise exception
-
else
-
throw :exception, exception
-
end
-
end
-
1
}
-
end
-
-
1
class ArgumentError < ::ArgumentError; end
-
-
1
class InvalidLocale < ArgumentError
-
1
attr_reader :locale
-
1
def initialize(locale)
-
@locale = locale
-
super "#{locale.inspect} is not a valid locale"
-
end
-
end
-
-
1
class InvalidLocaleData < ArgumentError
-
1
attr_reader :filename
-
1
def initialize(filename)
-
@filename = filename
-
super "can not load translations from #{filename}, expected it to return a hash, but does not"
-
end
-
end
-
-
1
class MissingTranslation
-
1
module Base
-
1
attr_reader :locale, :key, :options
-
-
1
def initialize(locale, key, options = nil)
-
@key, @locale, @options = key, locale, options.dup || {}
-
options.each { |k, v| self.options[k] = v.inspect if v.is_a?(Proc) }
-
end
-
-
1
def html_message
-
key = keys.last.to_s.gsub('_', ' ').gsub(/\b('?[a-z])/) { $1.capitalize }
-
%(<span class="translation_missing" title="translation missing: #{keys.join('.')}">#{key}</span>)
-
end
-
-
1
def keys
-
@keys ||= I18n.normalize_keys(locale, key, options[:scope]).tap do |keys|
-
keys << 'no key' if keys.size < 2
-
end
-
end
-
-
1
def message
-
"translation missing: #{keys.join('.')}"
-
end
-
1
alias :to_s :message
-
-
1
def to_exception
-
MissingTranslationData.new(locale, key, options)
-
end
-
end
-
-
1
include Base
-
end
-
-
1
class MissingTranslationData < ArgumentError
-
1
include MissingTranslation::Base
-
end
-
-
1
class InvalidPluralizationData < ArgumentError
-
1
attr_reader :entry, :count
-
1
def initialize(entry, count)
-
@entry, @count = entry, count
-
super "translation data #{entry.inspect} can not be used with :count => #{count}"
-
end
-
end
-
-
1
class MissingInterpolationArgument < ArgumentError
-
1
attr_reader :values, :string
-
1
def initialize(values, string)
-
@values, @string = values, string
-
super "missing interpolation argument in #{string.inspect} (#{values.inspect} given)"
-
end
-
end
-
-
1
class ReservedInterpolationKey < ArgumentError
-
1
attr_reader :key, :string
-
1
def initialize(key, string)
-
@key, @string = key, string
-
super "reserved key #{key.inspect} used in #{string.inspect}"
-
end
-
end
-
-
1
class UnknownFileType < ArgumentError
-
1
attr_reader :type, :filename
-
1
def initialize(type, filename)
-
@type, @filename = type, filename
-
super "can not load translations from #{filename}, the file type #{type} is not known"
-
end
-
end
-
end
-
# heavily based on Masao Mutoh's gettext String interpolation extension
-
# http://github.com/mutoh/gettext/blob/f6566738b981fe0952548c421042ad1e0cdfb31e/lib/gettext/core_ext/string.rb
-
-
1
module I18n
-
1
INTERPOLATION_PATTERN = Regexp.union(
-
/%%/,
-
/%\{(\w+)\}/, # matches placeholders like "%{foo}"
-
/%<(\w+)>(.*?\d*\.?\d*[bBdiouxXeEfgGcps])/ # matches placeholders like "%<foo>.d"
-
)
-
-
1
class << self
-
1
def interpolate(string, values)
-
1
raise ReservedInterpolationKey.new($1.to_sym, string) if string =~ RESERVED_KEYS_PATTERN
-
1
raise ArgumentError.new('Interpolation values must be a Hash.') unless values.kind_of?(Hash)
-
1
interpolate_hash(string, values)
-
end
-
-
1
def interpolate_hash(string, values)
-
1
string.gsub(INTERPOLATION_PATTERN) do |match|
-
1
if match == '%%'
-
'%'
-
else
-
1
key = ($1 || $2).to_sym
-
1
value = values.key?(key) ? values[key] : raise(MissingInterpolationArgument.new(values, string))
-
1
value = value.call(values) if value.respond_to?(:call)
-
1
$3 ? sprintf("%#{$3}", value) : value
-
end
-
end
-
end
-
end
-
end
-
1
module I18n
-
1
VERSION = "0.6.1"
-
end
-
##
-
# = JavaScript Object Notation (JSON)
-
#
-
# JSON is a lightweight data-interchange format. It is easy for us
-
# humans to read and write. Plus, equally simple for machines to generate or parse.
-
# JSON is completely language agnostic, making it the ideal interchange format.
-
#
-
# Built on two universally available structures:
-
# 1. A collection of name/value pairs. Often referred to as an _object_, hash table, record, struct, keyed list, or associative array.
-
# 2. An ordered list of values. More commonly called an _array_, vector, sequence or list.
-
#
-
# To read more about JSON visit: http://json.org
-
#
-
# == Parsing JSON
-
#
-
# To parse a JSON string received by another application or generated within
-
# your existing application:
-
#
-
# require 'json'
-
#
-
# my_hash = JSON.parse('{"hello": "goodbye"}')
-
# puts my_hash["hello"] => "goodbye"
-
#
-
# Notice the extra quotes <tt>''</tt> around the hash notation. Ruby expects
-
# the argument to be a string and can't convert objects like a hash or array.
-
#
-
# Ruby converts your string into a hash
-
#
-
# == Generating JSON
-
#
-
# Creating a JSON string for communication or serialization is
-
# just as simple.
-
#
-
# require 'json'
-
#
-
# my_hash = {:hello => "goodbye"}
-
# puts JSON.generate(my_hash) => "{\"hello\":\"goodbye\"}"
-
#
-
# Or an alternative way:
-
#
-
# require 'json'
-
# puts {:hello => "goodbye"}.to_json => "{\"hello\":\"goodbye\"}"
-
#
-
# <tt>JSON.generate</tt> only allows objects or arrays to be converted
-
# to JSON syntax. <tt>to_json</tt>, however, accepts many Ruby classes
-
# even though it acts only as a method for serialization:
-
#
-
# require 'json'
-
#
-
# 1.to_json => "1"
-
#
-
-
1
require 'json/common'
-
1
module JSON
-
1
require 'json/version'
-
-
1
begin
-
1
require 'json/ext'
-
rescue LoadError
-
require 'json/pure'
-
end
-
end
-
1
require 'json/version'
-
1
require 'json/generic_object'
-
-
1
module JSON
-
1
class << self
-
# If _object_ is string-like, parse the string and return the parsed result
-
# as a Ruby data structure. Otherwise generate a JSON text from the Ruby
-
# data structure object and return it.
-
#
-
# The _opts_ argument is passed through to generate/parse respectively. See
-
# generate and parse for their documentation.
-
1
def [](object, opts = {})
-
if object.respond_to? :to_str
-
JSON.parse(object.to_str, opts)
-
else
-
JSON.generate(object, opts)
-
end
-
end
-
-
# Returns the JSON parser class that is used by JSON. This is either
-
# JSON::Ext::Parser or JSON::Pure::Parser.
-
1
attr_reader :parser
-
-
# Set the JSON parser class _parser_ to be used by JSON.
-
1
def parser=(parser) # :nodoc:
-
1
@parser = parser
-
1
remove_const :Parser if JSON.const_defined_in?(self, :Parser)
-
1
const_set :Parser, parser
-
end
-
-
# Return the constant located at _path_. The format of _path_ has to be
-
# either ::A::B::C or A::B::C. In any case, A has to be located at the top
-
# level (absolute namespace path?). If there doesn't exist a constant at
-
# the given path, an ArgumentError is raised.
-
1
def deep_const_get(path) # :nodoc:
-
10
path.to_s.split(/::/).inject(Object) do |p, c|
-
case
-
when c.empty? then p
-
10
when JSON.const_defined_in?(p, c) then p.const_get(c)
-
else
-
begin
-
p.const_missing(c)
-
rescue NameError => e
-
raise ArgumentError, "can't get const #{path}: #{e}"
-
end
-
10
end
-
end
-
end
-
-
# Set the module _generator_ to be used by JSON.
-
1
def generator=(generator) # :nodoc:
-
1
old, $VERBOSE = $VERBOSE, nil
-
1
@generator = generator
-
1
generator_methods = generator::GeneratorMethods
-
1
for const in generator_methods.constants
-
10
klass = deep_const_get(const)
-
10
modul = generator_methods.const_get(const)
-
10
klass.class_eval do
-
10
instance_methods(false).each do |m|
-
507
m.to_s == 'to_json' and remove_method m
-
end
-
10
include modul
-
end
-
end
-
1
self.state = generator::State
-
1
const_set :State, self.state
-
1
const_set :SAFE_STATE_PROTOTYPE, State.new
-
1
const_set :FAST_STATE_PROTOTYPE, State.new(
-
:indent => '',
-
:space => '',
-
:object_nl => "",
-
:array_nl => "",
-
:max_nesting => false
-
)
-
1
const_set :PRETTY_STATE_PROTOTYPE, State.new(
-
:indent => ' ',
-
:space => ' ',
-
:object_nl => "\n",
-
:array_nl => "\n"
-
)
-
ensure
-
1
$VERBOSE = old
-
end
-
-
# Returns the JSON generator module that is used by JSON. This is
-
# either JSON::Ext::Generator or JSON::Pure::Generator.
-
1
attr_reader :generator
-
-
# Returns the JSON generator state class that is used by JSON. This is
-
# either JSON::Ext::Generator::State or JSON::Pure::Generator::State.
-
1
attr_accessor :state
-
-
# This is create identifier, which is used to decide if the _json_create_
-
# hook of a class should be called. It defaults to 'json_class'.
-
1
attr_accessor :create_id
-
end
-
1
self.create_id = 'json_class'
-
-
1
NaN = 0.0/0
-
-
1
Infinity = 1.0/0
-
-
1
MinusInfinity = -Infinity
-
-
# The base exception for JSON errors.
-
1
class JSONError < StandardError
-
1
def self.wrap(exception)
-
obj = new("Wrapped(#{exception.class}): #{exception.message.inspect}")
-
obj.set_backtrace exception.backtrace
-
obj
-
end
-
end
-
-
# This exception is raised if a parser error occurs.
-
1
class ParserError < JSONError; end
-
-
# This exception is raised if the nesting of parsed data structures is too
-
# deep.
-
1
class NestingError < ParserError; end
-
-
# :stopdoc:
-
1
class CircularDatastructure < NestingError; end
-
# :startdoc:
-
-
# This exception is raised if a generator or unparser error occurs.
-
1
class GeneratorError < JSONError; end
-
# For backwards compatibility
-
1
UnparserError = GeneratorError
-
-
# This exception is raised if the required unicode support is missing on the
-
# system. Usually this means that the iconv library is not installed.
-
1
class MissingUnicodeSupport < JSONError; end
-
-
1
module_function
-
-
# Parse the JSON document _source_ into a Ruby data structure and return it.
-
#
-
# _opts_ can have the following
-
# keys:
-
# * *max_nesting*: The maximum depth of nesting allowed in the parsed data
-
# structures. Disable depth checking with :max_nesting => false. It defaults
-
# to 19.
-
# * *allow_nan*: If set to true, allow NaN, Infinity and -Infinity in
-
# defiance of RFC 4627 to be parsed by the Parser. This option defaults
-
# to false.
-
# * *symbolize_names*: If set to true, returns symbols for the names
-
# (keys) in a JSON object. Otherwise strings are returned. Strings are
-
# the default.
-
# * *create_additions*: If set to false, the Parser doesn't create
-
# additions even if a matching class and create_id was found. This option
-
# defaults to true.
-
# * *object_class*: Defaults to Hash
-
# * *array_class*: Defaults to Array
-
1
def parse(source, opts = {})
-
Parser.new(source, opts).parse
-
end
-
-
# Parse the JSON document _source_ into a Ruby data structure and return it.
-
# The bang version of the parse method defaults to the more dangerous values
-
# for the _opts_ hash, so be sure only to parse trusted _source_ documents.
-
#
-
# _opts_ can have the following keys:
-
# * *max_nesting*: The maximum depth of nesting allowed in the parsed data
-
# structures. Enable depth checking with :max_nesting => anInteger. The parse!
-
# methods defaults to not doing max depth checking: This can be dangerous
-
# if someone wants to fill up your stack.
-
# * *allow_nan*: If set to true, allow NaN, Infinity, and -Infinity in
-
# defiance of RFC 4627 to be parsed by the Parser. This option defaults
-
# to true.
-
# * *create_additions*: If set to false, the Parser doesn't create
-
# additions even if a matching class and create_id was found. This option
-
# defaults to true.
-
1
def parse!(source, opts = {})
-
opts = {
-
:max_nesting => false,
-
:allow_nan => true
-
}.update(opts)
-
Parser.new(source, opts).parse
-
end
-
-
# Generate a JSON document from the Ruby data structure _obj_ and return
-
# it. _state_ is * a JSON::State object,
-
# * or a Hash like object (responding to to_hash),
-
# * an object convertible into a hash by a to_h method,
-
# that is used as or to configure a State object.
-
#
-
# It defaults to a state object, that creates the shortest possible JSON text
-
# in one line, checks for circular data structures and doesn't allow NaN,
-
# Infinity, and -Infinity.
-
#
-
# A _state_ hash can have the following keys:
-
# * *indent*: a string used to indent levels (default: ''),
-
# * *space*: a string that is put after, a : or , delimiter (default: ''),
-
# * *space_before*: a string that is put before a : pair delimiter (default: ''),
-
# * *object_nl*: a string that is put at the end of a JSON object (default: ''),
-
# * *array_nl*: a string that is put at the end of a JSON array (default: ''),
-
# * *allow_nan*: true if NaN, Infinity, and -Infinity should be
-
# generated, otherwise an exception is thrown if these values are
-
# encountered. This options defaults to false.
-
# * *max_nesting*: The maximum depth of nesting allowed in the data
-
# structures from which JSON is to be generated. Disable depth checking
-
# with :max_nesting => false, it defaults to 19.
-
#
-
# See also the fast_generate for the fastest creation method with the least
-
# amount of sanity checks, and the pretty_generate method for some
-
# defaults for pretty output.
-
1
def generate(obj, opts = nil)
-
if State === opts
-
state, opts = opts, nil
-
else
-
state = SAFE_STATE_PROTOTYPE.dup
-
end
-
if opts
-
if opts.respond_to? :to_hash
-
opts = opts.to_hash
-
elsif opts.respond_to? :to_h
-
opts = opts.to_h
-
else
-
raise TypeError, "can't convert #{opts.class} into Hash"
-
end
-
state = state.configure(opts)
-
end
-
state.generate(obj)
-
end
-
-
# :stopdoc:
-
# I want to deprecate these later, so I'll first be silent about them, and
-
# later delete them.
-
1
alias unparse generate
-
1
module_function :unparse
-
# :startdoc:
-
-
# Generate a JSON document from the Ruby data structure _obj_ and return it.
-
# This method disables the checks for circles in Ruby objects.
-
#
-
# *WARNING*: Be careful not to pass any Ruby data structures with circles as
-
# _obj_ argument because this will cause JSON to go into an infinite loop.
-
1
def fast_generate(obj, opts = nil)
-
if State === opts
-
state, opts = opts, nil
-
else
-
state = FAST_STATE_PROTOTYPE.dup
-
end
-
if opts
-
if opts.respond_to? :to_hash
-
opts = opts.to_hash
-
elsif opts.respond_to? :to_h
-
opts = opts.to_h
-
else
-
raise TypeError, "can't convert #{opts.class} into Hash"
-
end
-
state.configure(opts)
-
end
-
state.generate(obj)
-
end
-
-
# :stopdoc:
-
# I want to deprecate these later, so I'll first be silent about them, and later delete them.
-
1
alias fast_unparse fast_generate
-
1
module_function :fast_unparse
-
# :startdoc:
-
-
# Generate a JSON document from the Ruby data structure _obj_ and return it.
-
# The returned document is a prettier form of the document returned by
-
# #unparse.
-
#
-
# The _opts_ argument can be used to configure the generator. See the
-
# generate method for a more detailed explanation.
-
1
def pretty_generate(obj, opts = nil)
-
if State === opts
-
state, opts = opts, nil
-
else
-
state = PRETTY_STATE_PROTOTYPE.dup
-
end
-
if opts
-
if opts.respond_to? :to_hash
-
opts = opts.to_hash
-
elsif opts.respond_to? :to_h
-
opts = opts.to_h
-
else
-
raise TypeError, "can't convert #{opts.class} into Hash"
-
end
-
state.configure(opts)
-
end
-
state.generate(obj)
-
end
-
-
# :stopdoc:
-
# I want to deprecate these later, so I'll first be silent about them, and later delete them.
-
1
alias pretty_unparse pretty_generate
-
1
module_function :pretty_unparse
-
# :startdoc:
-
-
1
class << self
-
# The global default options for the JSON.load method:
-
# :max_nesting: false
-
# :allow_nan: true
-
# :quirks_mode: true
-
1
attr_accessor :load_default_options
-
end
-
1
self.load_default_options = {
-
:max_nesting => false,
-
:allow_nan => true,
-
:quirks_mode => true,
-
}
-
-
# Load a ruby data structure from a JSON _source_ and return it. A source can
-
# either be a string-like object, an IO-like object, or an object responding
-
# to the read method. If _proc_ was given, it will be called with any nested
-
# Ruby object as an argument recursively in depth first order. The default
-
# options for the parser can be changed via the load_default_options method.
-
#
-
# This method is part of the implementation of the load/dump interface of
-
# Marshal and YAML.
-
1
def load(source, proc = nil)
-
opts = load_default_options
-
if source.respond_to? :to_str
-
source = source.to_str
-
elsif source.respond_to? :to_io
-
source = source.to_io.read
-
elsif source.respond_to?(:read)
-
source = source.read
-
end
-
if opts[:quirks_mode] && (source.nil? || source.empty?)
-
source = 'null'
-
end
-
result = parse(source, opts)
-
recurse_proc(result, &proc) if proc
-
result
-
end
-
-
# Recursively calls passed _Proc_ if the parsed data structure is an _Array_ or _Hash_
-
1
def recurse_proc(result, &proc)
-
case result
-
when Array
-
result.each { |x| recurse_proc x, &proc }
-
proc.call result
-
when Hash
-
result.each { |x, y| recurse_proc x, &proc; recurse_proc y, &proc }
-
proc.call result
-
else
-
proc.call result
-
end
-
end
-
-
1
alias restore load
-
1
module_function :restore
-
-
1
class << self
-
# The global default options for the JSON.dump method:
-
# :max_nesting: false
-
# :allow_nan: true
-
# :quirks_mode: true
-
1
attr_accessor :dump_default_options
-
end
-
1
self.dump_default_options = {
-
:max_nesting => false,
-
:allow_nan => true,
-
:quirks_mode => true,
-
}
-
-
# Dumps _obj_ as a JSON string, i.e. calls generate on the object and returns
-
# the result.
-
#
-
# If anIO (an IO-like object or an object that responds to the write method)
-
# was given, the resulting JSON is written to it.
-
#
-
# If the number of nested arrays or objects exceeds _limit_, an ArgumentError
-
# exception is raised. This argument is similar (but not exactly the
-
# same!) to the _limit_ argument in Marshal.dump.
-
#
-
# The default options for the generator can be changed via the
-
# dump_default_options method.
-
#
-
# This method is part of the implementation of the load/dump interface of
-
# Marshal and YAML.
-
1
def dump(obj, anIO = nil, limit = nil)
-
if anIO and limit.nil?
-
anIO = anIO.to_io if anIO.respond_to?(:to_io)
-
unless anIO.respond_to?(:write)
-
limit = anIO
-
anIO = nil
-
end
-
end
-
opts = JSON.dump_default_options
-
limit and opts.update(:max_nesting => limit)
-
result = generate(obj, opts)
-
if anIO
-
anIO.write result
-
anIO
-
else
-
result
-
end
-
rescue JSON::NestingError
-
raise ArgumentError, "exceed depth limit"
-
end
-
-
# Swap consecutive bytes of _string_ in place.
-
1
def self.swap!(string) # :nodoc:
-
0.upto(string.size / 2) do |i|
-
break unless string[2 * i + 1]
-
string[2 * i], string[2 * i + 1] = string[2 * i + 1], string[2 * i]
-
end
-
string
-
end
-
-
# Shortuct for iconv.
-
if ::String.method_defined?(:encode) &&
-
# XXX Rubinius doesn't support ruby 1.9 encoding yet
-
1
defined?(RUBY_ENGINE) && RUBY_ENGINE != 'rbx'
-
then
-
# Encodes string using Ruby's _String.encode_
-
1
def self.iconv(to, from, string)
-
string.encode(to, from)
-
end
-
else
-
require 'iconv'
-
# Encodes string using _iconv_ library
-
def self.iconv(to, from, string)
-
Iconv.conv(to, from, string)
-
end
-
end
-
-
1
if ::Object.method(:const_defined?).arity == 1
-
def self.const_defined_in?(modul, constant)
-
modul.const_defined?(constant)
-
end
-
else
-
1
def self.const_defined_in?(modul, constant)
-
11
modul.const_defined?(constant, false)
-
end
-
end
-
end
-
-
1
module ::Kernel
-
1
private
-
-
# Outputs _objs_ to STDOUT as JSON strings in the shortest form, that is in
-
# one line.
-
1
def j(*objs)
-
objs.each do |obj|
-
puts JSON::generate(obj, :allow_nan => true, :max_nesting => false)
-
end
-
nil
-
end
-
-
# Ouputs _objs_ to STDOUT as JSON strings in a pretty format, with
-
# indentation and over many lines.
-
1
def jj(*objs)
-
objs.each do |obj|
-
puts JSON::pretty_generate(obj, :allow_nan => true, :max_nesting => false)
-
end
-
nil
-
end
-
-
# If _object_ is string-like, parse the string and return the parsed result as
-
# a Ruby data structure. Otherwise, generate a JSON text from the Ruby data
-
# structure object and return it.
-
#
-
# The _opts_ argument is passed through to generate/parse respectively. See
-
# generate and parse for their documentation.
-
1
def JSON(object, *args)
-
if object.respond_to? :to_str
-
JSON.parse(object.to_str, args.first)
-
else
-
JSON.generate(object, args.first)
-
end
-
end
-
end
-
-
# Extends any Class to include _json_creatable?_ method.
-
1
class ::Class
-
# Returns true if this class can be used to create an instance
-
# from a serialised JSON string. The class has to implement a class
-
# method _json_create_ that expects a hash as first parameter. The hash
-
# should include the required data.
-
1
def json_creatable?
-
respond_to?(:json_create)
-
end
-
end
-
1
if ENV['SIMPLECOV_COVERAGE'].to_i == 1
-
require 'simplecov'
-
SimpleCov.start do
-
add_filter "/tests/"
-
end
-
end
-
1
require 'json/common'
-
-
1
module JSON
-
# This module holds all the modules/classes that implement JSON's
-
# functionality as C extensions.
-
1
module Ext
-
1
require 'json/ext/parser'
-
1
require 'json/ext/generator'
-
1
$DEBUG and warn "Using Ext extension for JSON."
-
1
JSON.parser = Parser
-
1
JSON.generator = Generator
-
end
-
-
1
JSON_LOADED = true unless defined?(::JSON::JSON_LOADED)
-
end
-
1
require 'ostruct'
-
-
1
module JSON
-
1
class GenericObject < OpenStruct
-
1
class << self
-
1
alias [] new
-
-
1
def json_create(data)
-
data = data.dup
-
data.delete JSON.create_id
-
self[data]
-
end
-
end
-
-
1
def to_hash
-
table
-
end
-
-
1
def [](name)
-
table[name.to_sym]
-
end
-
-
1
def []=(name, value)
-
__send__ "#{name}=", value
-
end
-
-
1
def |(other)
-
self.class[other.to_hash.merge(to_hash)]
-
end
-
-
1
def as_json(*)
-
{ JSON.create_id => self.class.name }.merge to_hash
-
end
-
-
1
def to_json(*a)
-
as_json.to_json(*a)
-
end
-
end
-
end
-
1
module JSON
-
# JSON version
-
1
VERSION = '1.7.5'
-
4
VERSION_ARRAY = VERSION.split(/\./).map { |x| x.to_i } # :nodoc:
-
1
VERSION_MAJOR = VERSION_ARRAY[0] # :nodoc:
-
1
VERSION_MINOR = VERSION_ARRAY[1] # :nodoc:
-
1
VERSION_BUILD = VERSION_ARRAY[2] # :nodoc:
-
end
-
# encoding: utf-8
-
1
module Mail # :doc:
-
-
1
require 'date'
-
1
require 'shellwords'
-
-
1
require 'uri'
-
1
require 'net/smtp'
-
1
require 'mime/types'
-
-
1
if RUBY_VERSION <= '1.8.6'
-
begin
-
require 'tlsmail'
-
rescue LoadError
-
raise "You need to install tlsmail if you are using ruby <= 1.8.6"
-
end
-
end
-
-
1
if RUBY_VERSION >= "1.9.0"
-
1
require 'mail/version_specific/ruby_1_9'
-
1
RubyVer = Ruby19
-
else
-
require 'mail/version_specific/ruby_1_8'
-
RubyVer = Ruby18
-
end
-
-
1
require 'mail/version'
-
-
1
require 'mail/core_extensions/nil'
-
1
require 'mail/core_extensions/object'
-
1
require 'mail/core_extensions/string'
-
1
require 'mail/core_extensions/shell_escape'
-
1
require 'mail/core_extensions/smtp' if RUBY_VERSION < '1.9.3'
-
1
require 'mail/indifferent_hash'
-
-
# Only load our multibyte extensions if AS is not already loaded
-
1
if defined?(ActiveSupport)
-
1
require 'active_support/inflector'
-
else
-
require 'mail/core_extensions/string/access'
-
require 'mail/core_extensions/string/multibyte'
-
require 'mail/multibyte'
-
end
-
-
1
require 'mail/patterns'
-
1
require 'mail/utilities'
-
1
require 'mail/configuration'
-
-
# Autoload mail send and receive classes.
-
1
require 'mail/network'
-
-
1
require 'mail/message'
-
1
require 'mail/part'
-
1
require 'mail/header'
-
1
require 'mail/parts_list'
-
1
require 'mail/attachments_list'
-
1
require 'mail/body'
-
1
require 'mail/field'
-
1
require 'mail/field_list'
-
-
1
require 'mail/envelope'
-
-
1
parsers = %w[ rfc2822_obsolete rfc2822 address_lists phrase_lists
-
date_time received message_ids envelope_from rfc2045
-
mime_version content_type content_disposition
-
content_transfer_encoding content_location ]
-
-
1
parsers.each do |parser|
-
14
begin
-
# Try requiring the pre-compiled ruby version first
-
14
require 'treetop/runtime'
-
14
require "mail/parsers/#{parser}"
-
rescue LoadError
-
# Otherwise, get treetop to compile and load it
-
require 'treetop/runtime'
-
require 'treetop/compiler'
-
Treetop.load(File.join(File.dirname(__FILE__)) + "/mail/parsers/#{parser}")
-
end
-
end
-
-
# Autoload header field elements and transfer encodings.
-
1
require 'mail/elements'
-
1
require 'mail/encodings'
-
1
require 'mail/encodings/base64'
-
1
require 'mail/encodings/quoted_printable'
-
-
1
require 'mail/matchers/has_sent_mail'
-
-
# Finally... require all the Mail.methods
-
1
require 'mail/mail'
-
end
-
# encoding: utf-8
-
1
module Mail
-
-
# = Body
-
#
-
# The body is where the text of the email is stored. Mail treats the body
-
# as a single object. The body itself has no information about boundaries
-
# used in the MIME standard, it just looks at it's content as either a single
-
# block of text, or (if it is a multipart message) as an array of blocks o text.
-
#
-
# A body has to be told to split itself up into a multipart message by calling
-
# #split with the correct boundary. This is because the body object has no way
-
# of knowing what the correct boundary is for itself (there could be many
-
# boundaries in a body in the case of a nested MIME text).
-
#
-
# Once split is called, Mail::Body will slice itself up on this boundary,
-
# assigning anything that appears before the first part to the preamble, and
-
# anything that appears after the closing boundary to the epilogue, then
-
# each part gets initialized into a Mail::Part object.
-
#
-
# The boundary that is used to split up the Body is also stored in the Body
-
# object for use on encoding itself back out to a string. You can
-
# overwrite this if it needs to be changed.
-
#
-
# On encoding, the body will return the preamble, then each part joined by
-
# the boundary, followed by a closing boundary string and then the epilogue.
-
1
class Body
-
-
1
def initialize(string = '')
-
386
@boundary = nil
-
386
@preamble = nil
-
386
@epilogue = nil
-
386
@charset = nil
-
386
@part_sort_order = [ "text/plain", "text/enriched", "text/html" ]
-
386
@parts = Mail::PartsList.new
-
386
if string.blank?
-
256
@raw_source = ''
-
else
-
# Do join first incase we have been given an Array in Ruby 1.9
-
130
if string.respond_to?(:join)
-
@raw_source = string.join('')
-
elsif string.respond_to?(:to_s)
-
130
@raw_source = string.to_s
-
else
-
raise "You can only assign a string or an object that responds_to? :join or :to_s to a body."
-
end
-
end
-
386
@encoding = (only_us_ascii? ? '7bit' : '8bit')
-
386
set_charset
-
end
-
-
# Matches this body with another body. Also matches the decoded value of this
-
# body with a string.
-
#
-
# Examples:
-
#
-
# body = Mail::Body.new('The body')
-
# body == body #=> true
-
#
-
# body = Mail::Body.new('The body')
-
# body == 'The body' #=> true
-
#
-
# body = Mail::Body.new("VGhlIGJvZHk=\n")
-
# body.encoding = 'base64'
-
# body == "The body" #=> true
-
1
def ==(other)
-
if other.class == String
-
self.decoded == other
-
else
-
super
-
end
-
end
-
-
# Accepts a string and performs a regular expression against the decoded text
-
#
-
# Examples:
-
#
-
# body = Mail::Body.new('The body')
-
# body =~ /The/ #=> 0
-
#
-
# body = Mail::Body.new("VGhlIGJvZHk=\n")
-
# body.encoding = 'base64'
-
# body =~ /The/ #=> 0
-
1
def =~(regexp)
-
self.decoded =~ regexp
-
end
-
-
# Accepts a string and performs a regular expression against the decoded text
-
#
-
# Examples:
-
#
-
# body = Mail::Body.new('The body')
-
# body.match(/The/) #=> #<MatchData "The">
-
#
-
# body = Mail::Body.new("VGhlIGJvZHk=\n")
-
# body.encoding = 'base64'
-
# body.match(/The/) #=> #<MatchData "The">
-
1
def match(regexp)
-
self.decoded.match(regexp)
-
end
-
-
# Accepts anything that responds to #to_s and checks if it's a substring of the decoded text
-
#
-
# Examples:
-
#
-
# body = Mail::Body.new('The body')
-
# body.include?('The') #=> true
-
#
-
# body = Mail::Body.new("VGhlIGJvZHk=\n")
-
# body.encoding = 'base64'
-
# body.include?('The') #=> true
-
1
def include?(other)
-
self.decoded.include?(other.to_s)
-
end
-
-
# Allows you to set the sort order of the parts, overriding the default sort order.
-
# Defaults to 'text/plain', then 'text/enriched', then 'text/html' with any other content
-
# type coming after.
-
1
def set_sort_order(order)
-
111
@part_sort_order = order
-
end
-
-
# Allows you to sort the parts according to the default sort order, or the sort order you
-
# set with :set_sort_order.
-
#
-
# sort_parts! is also called from :encode, so there is no need for you to call this explicitly
-
1
def sort_parts!
-
116
@parts.each do |p|
-
80
p.body.set_sort_order(@part_sort_order)
-
80
@parts.sort!(@part_sort_order)
-
80
p.body.sort_parts!
-
end
-
end
-
-
# Returns the raw source that the body was initialized with, without
-
# any tampering
-
1
def raw_source
-
1100
@raw_source
-
end
-
-
1
def get_best_encoding(target)
-
178
target_encoding = Mail::Encodings.get_encoding(target)
-
178
target_encoding.get_best_compatible(encoding, raw_source)
-
end
-
-
# Returns a body encoded using transfer_encoding. Multipart always uses an
-
# identiy encoding (i.e. no encoding).
-
# Calling this directly is not a good idea, but supported for compatibility
-
# TODO: Validate that preamble and epilogue are valid for requested encoding
-
1
def encoded(transfer_encoding = '8bit')
-
106
if multipart?
-
5
self.sort_parts!
-
15
encoded_parts = parts.map { |p| p.encoded }
-
5
([preamble] + encoded_parts).join(crlf_boundary) + end_boundary + epilogue.to_s
-
else
-
101
be = get_best_encoding(transfer_encoding)
-
101
dec = Mail::Encodings::get_encoding(encoding)
-
101
enc = Mail::Encodings::get_encoding(be)
-
101
if transfer_encoding == encoding and dec.nil?
-
# Cannot decode, so skip normalization
-
raw_source
-
else
-
# Decode then encode to normalize and allow transforming
-
# from base64 to Q-P and vice versa
-
101
decoded = dec.decode(raw_source)
-
101
if defined?(Encoding) && charset && charset != "US-ASCII"
-
decoded.encode!(charset)
-
decoded.force_encoding('BINARY') unless Encoding.find(charset).ascii_compatible?
-
end
-
101
enc.encode(decoded)
-
end
-
end
-
end
-
-
1
def decoded
-
45
if !Encodings.defined?(encoding)
-
raise UnknownEncodingType, "Don't know how to decode #{encoding}, please call #encoded and decode it yourself."
-
else
-
45
Encodings.get_encoding(encoding).decode(raw_source)
-
end
-
end
-
-
1
def to_s
-
14
decoded
-
end
-
-
1
def charset
-
201
@charset
-
end
-
-
1
def charset=( val )
-
@charset = val
-
end
-
-
1
def encoding(val = nil)
-
470
if val
-
self.encoding = val
-
else
-
470
@encoding
-
end
-
end
-
-
1
def encoding=( val )
-
28
@encoding = if val == "text" || val.blank?
-
(only_us_ascii? ? '7bit' : '8bit')
-
else
-
28
val
-
end
-
end
-
-
# Returns the preamble (any text that is before the first MIME boundary)
-
1
def preamble
-
5
@preamble
-
end
-
-
# Sets the preamble to a string (adds text before the first MIME boundary)
-
1
def preamble=( val )
-
@preamble = val
-
end
-
-
# Returns the epilogue (any text that is after the last MIME boundary)
-
1
def epilogue
-
5
@epilogue
-
end
-
-
# Sets the epilogue to a string (adds text after the last MIME boundary)
-
1
def epilogue=( val )
-
@epilogue = val
-
end
-
-
# Returns true if there are parts defined in the body
-
1
def multipart?
-
460
true unless parts.empty?
-
end
-
-
# Returns the boundary used by the body
-
1
def boundary
-
66
@boundary
-
end
-
-
# Allows you to change the boundary of this Body object
-
1
def boundary=( val )
-
34
@boundary = val
-
end
-
-
1
def parts
-
1164
@parts
-
end
-
-
1
def <<( val )
-
56
if @parts
-
56
@parts << val
-
else
-
@parts = Mail::PartsList.new[val]
-
end
-
end
-
-
1
def split!(boundary)
-
self.boundary = boundary
-
parts = raw_source.split("--#{boundary}")
-
# Make the preamble equal to the preamble (if any)
-
self.preamble = parts[0].to_s.strip
-
# Make the epilogue equal to the epilogue (if any)
-
self.epilogue = parts[-1].to_s.sub('--', '').strip
-
parts[1...-1].to_a.each { |part| @parts << Mail::Part.new(part) }
-
self
-
end
-
-
1
def only_us_ascii?
-
772
!(raw_source =~ /[^\x01-\x7f]/)
-
end
-
-
1
def empty?
-
2
!!raw_source.to_s.empty?
-
end
-
-
1
private
-
-
1
def crlf_boundary
-
5
"\r\n\r\n--#{boundary}\r\n"
-
end
-
-
1
def end_boundary
-
5
"\r\n\r\n--#{boundary}--\r\n"
-
end
-
-
1
def set_charset
-
386
only_us_ascii? ? @charset = 'US-ASCII' : @charset = nil
-
end
-
end
-
end
-
# encoding: utf-8
-
#
-
# Thanks to Nicolas Fouché for this wrapper
-
#
-
1
require 'singleton'
-
-
1
module Mail
-
-
# The Configuration class is a Singleton used to hold the default
-
# configuration for all Mail objects.
-
#
-
# Each new mail object gets a copy of these values at initialization
-
# which can be overwritten on a per mail object basis.
-
1
class Configuration
-
1
include Singleton
-
-
1
def initialize
-
1
@delivery_method = nil
-
1
@retriever_method = nil
-
1
super
-
end
-
-
1
def delivery_method(method = nil, settings = {})
-
235
return @delivery_method if @delivery_method && method.nil?
-
1
@delivery_method = lookup_delivery_method(method).new(settings)
-
end
-
-
1
def lookup_delivery_method(method)
-
119
case method
-
when nil
-
1
Mail::SMTP
-
when :smtp
-
Mail::SMTP
-
when :sendmail
-
Mail::Sendmail
-
when :exim
-
Mail::Exim
-
when :file
-
Mail::FileDelivery
-
when :smtp_connection
-
Mail::SMTPConnection
-
when :test
-
Mail::TestMailer
-
else
-
118
method
-
end
-
end
-
-
1
def retriever_method(method = nil, settings = {})
-
return @retriever_method if @retriever_method && method.nil?
-
@retriever_method = lookup_retriever_method(method).new(settings)
-
end
-
-
1
def lookup_retriever_method(method)
-
case method
-
when nil
-
Mail::POP3
-
when :pop3
-
Mail::POP3
-
when :imap
-
Mail::IMAP
-
when :test
-
Mail::TestRetriever
-
else
-
method
-
end
-
end
-
-
1
def param_encode_language(value = nil)
-
value ? @encode_language = value : @encode_language ||= 'en'
-
end
-
-
end
-
-
end
-
# encoding: utf-8
-
-
# This is not loaded if ActiveSupport is already loaded
-
-
1
class NilClass #:nodoc:
-
1
def blank?
-
1426
true
-
end
-
-
1
def to_crlf
-
230
''
-
end
-
-
1
def to_lf
-
''
-
end
-
end
-
# encoding: utf-8
-
-
# This is not loaded if ActiveSupport is already loaded
-
-
1
class Object
-
1
def blank?
-
144
if respond_to?(:empty?)
-
124
empty?
-
else
-
20
!self
-
end
-
end
-
end
-
# encoding: utf-8
-
-
# The following is an adaptation of ruby 1.9.2's shellwords.rb file,
-
# it is modified to include '+' in the allowed list to allow for
-
# sendmail to accept email addresses as the sender with a + in them
-
#
-
1
module Mail
-
1
module ShellEscape
-
# Escapes a string so that it can be safely used in a Bourne shell
-
# command line.
-
#
-
# Note that a resulted string should be used unquoted and is not
-
# intended for use in double quotes nor in single quotes.
-
#
-
# open("| grep #{Shellwords.escape(pattern)} file") { |pipe|
-
# # ...
-
# }
-
#
-
# +String#shellescape+ is a shorthand for this function.
-
#
-
# open("| grep #{pattern.shellescape} file") { |pipe|
-
# # ...
-
# }
-
#
-
1
def escape_for_shell(str)
-
# An empty argument will be skipped, so return empty quotes.
-
return "''" if str.empty?
-
-
str = str.dup
-
-
# Process as a single byte sequence because not all shell
-
# implementations are multibyte aware.
-
str.gsub!(/([^A-Za-z0-9_\s\+\-.,:\/@\n])/n, "\\\\\\1")
-
-
# A LF cannot be escaped with a backslash because a backslash + LF
-
# combo is regarded as line continuation and simply ignored.
-
str.gsub!(/\n/, "'\n'")
-
-
return str
-
end
-
-
1
module_function :escape_for_shell
-
end
-
end
-
-
1
class String
-
# call-seq:
-
# str.shellescape => string
-
#
-
# Escapes +str+ so that it can be safely used in a Bourne shell
-
# command line. See +Shellwords::shellescape+ for details.
-
#
-
1
def escape_for_shell
-
Mail::ShellEscape.escape_for_shell(self)
-
end
-
end
-
# encoding: utf-8
-
1
class String #:nodoc:
-
1
def to_crlf
-
438
to_str.gsub(/\n|\r\n|\r/) { "\r\n" }
-
end
-
-
1
def to_lf
-
192
to_str.gsub(/\n|\r\n|\r/) { "\n" }
-
end
-
-
140
unless String.instance_methods(false).map {|m| m.to_sym}.include?(:blank?)
-
def blank?
-
self !~ /\S/
-
end
-
end
-
-
1
unless method_defined?(:ascii_only?)
-
# Provides all strings with the Ruby 1.9 method of .ascii_only? and
-
# returns true or false
-
US_ASCII_REGEXP = %Q{\x00-\x7f}
-
def ascii_only?
-
!(self =~ /[^#{US_ASCII_REGEXP}]/)
-
end
-
end
-
-
1
def not_ascii_only?
-
90
!ascii_only?
-
end
-
-
1
unless method_defined?(:bytesize)
-
alias :bytesize :length
-
end
-
end
-
1
module Mail
-
1
autoload :Address, 'mail/elements/address'
-
1
autoload :AddressList, 'mail/elements/address_list'
-
1
autoload :ContentDispositionElement, 'mail/elements/content_disposition_element'
-
1
autoload :ContentLocationElement, 'mail/elements/content_location_element'
-
1
autoload :ContentTransferEncodingElement, 'mail/elements/content_transfer_encoding_element'
-
1
autoload :ContentTypeElement, 'mail/elements/content_type_element'
-
1
autoload :DateTimeElement, 'mail/elements/date_time_element'
-
1
autoload :EnvelopeFromElement, 'mail/elements/envelope_from_element'
-
1
autoload :MessageIdsElement, 'mail/elements/message_ids_element'
-
1
autoload :MimeVersionElement, 'mail/elements/mime_version_element'
-
1
autoload :PhraseList, 'mail/elements/phrase_list'
-
1
autoload :ReceivedElement, 'mail/elements/received_element'
-
end
-
# encoding: utf-8
-
1
module Mail
-
1
class Address
-
-
1
include Mail::Utilities
-
-
# Mail::Address handles all email addresses in Mail. It takes an email address string
-
# and parses it, breaking it down into it's component parts and allowing you to get the
-
# address, comments, display name, name, local part, domain part and fully formatted
-
# address.
-
#
-
# Mail::Address requires a correctly formatted email address per RFC2822 or RFC822. It
-
# handles all obsolete versions including obsolete domain routing on the local part.
-
#
-
# a = Address.new('Mikel Lindsaar (My email address) <mikel@test.lindsaar.net>')
-
# a.format #=> 'Mikel Lindsaar <mikel@test.lindsaar.net> (My email address)'
-
# a.address #=> 'mikel@test.lindsaar.net'
-
# a.display_name #=> 'Mikel Lindsaar'
-
# a.local #=> 'mikel'
-
# a.domain #=> 'test.lindsaar.net'
-
# a.comments #=> ['My email address']
-
# a.to_s #=> 'Mikel Lindsaar <mikel@test.lindsaar.net> (My email address)'
-
1
def initialize(value = nil)
-
103
@output_type = nil
-
103
@tree = nil
-
103
@raw_text = value
-
case
-
when value.nil?
-
@parsed = false
-
return
-
else
-
103
parse(value)
-
103
end
-
end
-
-
# Returns the raw imput of the passed in string, this is before it is passed
-
# by the parser.
-
1
def raw
-
@raw_text
-
end
-
-
# Returns a correctly formatted address for the email going out. If given
-
# an incorrectly formatted address as input, Mail::Address will do it's best
-
# to format it correctly. This includes quoting display names as needed and
-
# putting the address in angle brackets etc.
-
#
-
# a = Address.new('Mikel Lindsaar (My email address) <mikel@test.lindsaar.net>')
-
# a.format #=> 'Mikel Lindsaar <mikel@test.lindsaar.net> (My email address)'
-
1
def format
-
298
parse unless @parsed
-
case
-
when tree.nil?
-
''
-
when display_name
-
2
[quote_phrase(display_name), "<#{address}>", format_comments].compact.join(" ")
-
else
-
296
[address, format_comments].compact.join(" ")
-
298
end
-
end
-
-
# Returns the address that is in the address itself. That is, the
-
# local@domain string, without any angle brackets or the like.
-
#
-
# a = Address.new('Mikel Lindsaar (My email address) <mikel@test.lindsaar.net>')
-
# a.address #=> 'mikel@test.lindsaar.net'
-
1
def address
-
386
parse unless @parsed
-
386
domain ? "#{local}@#{domain}" : local
-
end
-
-
# Provides a way to assign an address to an already made Mail::Address object.
-
#
-
# a = Address.new
-
# a.address = 'Mikel Lindsaar (My email address) <mikel@test.lindsaar.net>'
-
# a.address #=> 'mikel@test.lindsaar.net'
-
1
def address=(value)
-
parse(value)
-
end
-
-
# Returns the display name of the email address passed in.
-
#
-
# a = Address.new('Mikel Lindsaar (My email address) <mikel@test.lindsaar.net>')
-
# a.display_name #=> 'Mikel Lindsaar'
-
1
def display_name
-
300
parse unless @parsed
-
300
@display_name ||= get_display_name
-
300
Encodings.decode_encode(@display_name.to_s, @output_type) if @display_name
-
end
-
-
# Provides a way to assign a display name to an already made Mail::Address object.
-
#
-
# a = Address.new
-
# a.address = 'mikel@test.lindsaar.net'
-
# a.display_name = 'Mikel Lindsaar'
-
# a.format #=> 'Mikel Lindsaar <mikel@test.lindsaar.net>'
-
1
def display_name=( str )
-
@display_name = str
-
end
-
-
# Returns the local part (the left hand side of the @ sign in the email address) of
-
# the address
-
#
-
# a = Address.new('Mikel Lindsaar (My email address) <mikel@test.lindsaar.net>')
-
# a.local #=> 'mikel'
-
1
def local
-
386
parse unless @parsed
-
386
"#{obs_domain_list}#{get_local.strip}" if get_local
-
end
-
-
# Returns the domain part (the right hand side of the @ sign in the email address) of
-
# the address
-
#
-
# a = Address.new('Mikel Lindsaar (My email address) <mikel@test.lindsaar.net>')
-
# a.domain #=> 'test.lindsaar.net'
-
1
def domain
-
772
parse unless @parsed
-
772
strip_all_comments(get_domain) if get_domain
-
end
-
-
# Returns an array of comments that are in the email, or an empty array if there
-
# are no comments
-
#
-
# a = Address.new('Mikel Lindsaar (My email address) <mikel@test.lindsaar.net>')
-
# a.comments #=> ['My email address']
-
1
def comments
-
1367
parse unless @parsed
-
1367
if get_comments.empty?
-
nil
-
else
-
get_comments.map { |c| c.squeeze(" ") }
-
end
-
end
-
-
# Sometimes an address will not have a display name, but might have the name
-
# as a comment field after the address. This returns that name if it exists.
-
#
-
# a = Address.new('mikel@test.lindsaar.net (Mikel Lindsaar)')
-
# a.name #=> 'Mikel Lindsaar'
-
1
def name
-
parse unless @parsed
-
get_name
-
end
-
-
# Returns the format of the address, or returns nothing
-
#
-
# a = Address.new('Mikel Lindsaar (My email address) <mikel@test.lindsaar.net>')
-
# a.format #=> 'Mikel Lindsaar <mikel@test.lindsaar.net> (My email address)'
-
1
def to_s
-
parse unless @parsed
-
format
-
end
-
-
# Shows the Address object basic details, including the Address
-
# a = Address.new('Mikel (My email) <mikel@test.lindsaar.net>')
-
# a.inspect #=> "#<Mail::Address:14184910 Address: |Mikel <mikel@test.lindsaar.net> (My email)| >"
-
1
def inspect
-
parse unless @parsed
-
"#<#{self.class}:#{self.object_id} Address: |#{to_s}| >"
-
end
-
-
1
def encoded
-
298
@output_type = :encode
-
298
format
-
end
-
-
1
def decoded
-
@output_type = :decode
-
format
-
end
-
-
1
private
-
-
1
def parse(value = nil)
-
103
@parsed = true
-
case
-
when value.nil?
-
nil
-
when value.class == String
-
self.tree = Mail::AddressList.new(value).address_nodes.first
-
else
-
103
self.tree = value
-
103
end
-
end
-
-
-
1
def get_domain
-
1544
if tree.respond_to?(:angle_addr) && tree.angle_addr.respond_to?(:addr_spec) && tree.angle_addr.addr_spec.respond_to?(:domain)
-
12
@domain_text ||= tree.angle_addr.addr_spec.domain.text_value.strip
-
1532
elsif tree.respond_to?(:domain)
-
1532
@domain_text ||= tree.domain.text_value.strip
-
elsif tree.respond_to?(:addr_spec) && tree.addr_spec.respond_to?(:domain)
-
tree.addr_spec.domain.text_value.strip
-
else
-
nil
-
end
-
end
-
-
1
def strip_all_comments(string)
-
773
unless comments.blank?
-
comments.each do |comment|
-
string = string.gsub("(#{comment})", '')
-
end
-
end
-
773
string.strip
-
end
-
-
1
def strip_domain_comments(value)
-
unless comments.blank?
-
comments.each do |comment|
-
if get_domain && get_domain.include?("(#{comment})")
-
value = value.gsub("(#{comment})", '')
-
end
-
end
-
end
-
value.to_s.strip
-
end
-
-
1
def get_comments
-
1367
if tree.respond_to?(:comments)
-
1367
@comments = tree.comments.map { |c| unparen(c.text_value.to_str) }
-
else
-
@comments = []
-
end
-
end
-
-
1
def get_display_name
-
297
if tree.respond_to?(:display_name)
-
1
name = unquote(tree.display_name.text_value.strip)
-
1
str = strip_all_comments(name.to_s)
-
elsif comments
-
if domain
-
str = strip_domain_comments(format_comments)
-
else
-
str = nil
-
end
-
else
-
296
nil
-
end
-
-
297
if str.blank?
-
nil
-
else
-
1
str
-
end
-
end
-
-
1
def get_name
-
if display_name
-
str = display_name
-
else
-
if comments
-
comment_text = comments.join(' ').squeeze(" ")
-
str = "(#{comment_text})"
-
end
-
end
-
-
if str.blank?
-
nil
-
else
-
unparen(str)
-
end
-
end
-
-
# Provides access to the Treetop parse tree for this address
-
1
def tree
-
11548
@tree
-
end
-
-
1
def tree=(value)
-
103
@tree = value
-
end
-
-
1
def format_comments
-
298
if comments
-
comment_text = comments.map {|c| escape_paren(c) }.join(' ').squeeze(" ")
-
@format_comments ||= "(#{comment_text})"
-
else
-
nil
-
end
-
end
-
-
1
def obs_domain_list
-
386
if tree.respond_to?(:angle_addr)
-
18
obs = tree.angle_addr.elements.select { |e| e.respond_to?(:obs_domain_list) }
-
3
!obs.empty? ? obs.first.text_value : nil
-
else
-
nil
-
end
-
end
-
-
1
def get_local
-
case
-
when tree.respond_to?(:local_dot_atom_text)
-
tree.local_dot_atom_text.text_value
-
when tree.respond_to?(:angle_addr) && tree.angle_addr.respond_to?(:addr_spec) && tree.angle_addr.addr_spec.respond_to?(:local_part)
-
6
tree.angle_addr.addr_spec.local_part.text_value
-
when tree.respond_to?(:addr_spec) && tree.addr_spec.respond_to?(:local_part)
-
tree.addr_spec.local_part.text_value
-
else
-
766
tree && tree.respond_to?(:local_part) ? tree.local_part.text_value : nil
-
772
end
-
end
-
-
-
end
-
end
-
# encoding: utf-8
-
1
module Mail
-
1
class AddressList # :nodoc:
-
-
# Mail::AddressList is the class that parses To, From and other address fields from
-
# emails passed into Mail.
-
#
-
# AddressList provides a way to query the groups and mailbox lists of the passed in
-
# string.
-
#
-
# It can supply all addresses in an array, or return each address as an address object.
-
#
-
# Mail::AddressList requires a correctly formatted group or mailbox list per RFC2822 or
-
# RFC822. It also handles all obsolete versions in those RFCs.
-
#
-
# list = 'ada@test.lindsaar.net, My Group: mikel@test.lindsaar.net, Bob <bob@test.lindsaar.net>;'
-
# a = AddressList.new(list)
-
# a.addresses #=> [#<Mail::Address:14943130 Address: |ada@test.lindsaar.net...
-
# a.group_names #=> ["My Group"]
-
1
def initialize(string)
-
289
if string.blank?
-
@address_nodes = []
-
return self
-
end
-
289
parser = Mail::AddressListsParser.new
-
289
if tree = parser.parse(string)
-
289
@address_nodes = tree.addresses
-
else
-
raise Mail::Field::ParseError.new(AddressListsParser, string, parser.failure_reason)
-
end
-
end
-
-
# Returns a list of address objects from the parsed line
-
1
def addresses
-
@addresses ||= get_addresses.map do |address_tree|
-
103
Mail::Address.new(address_tree)
-
237
end
-
end
-
-
# Returns a list of all recipient syntax trees that are not part of a group
-
1
def individual_recipients # :nodoc:
-
103
@individual_recipients ||= @address_nodes - group_recipients
-
end
-
-
# Returns a list of all recipient syntax trees that are part of a group
-
1
def group_recipients # :nodoc:
-
607
@group_recipients ||= @address_nodes.select { |an| an.respond_to?(:group_name) }
-
end
-
-
# Returns the names as an array of strings of all groups
-
1
def group_names # :nodoc:
-
group_recipients.map { |g| g.group_name.text_value }
-
end
-
-
# Returns a list of address syntax trees
-
1
def address_nodes # :nodoc:
-
@address_nodes
-
end
-
-
1
private
-
-
1
def get_addresses
-
103
(individual_recipients + group_recipients.map { |g| get_group_addresses(g) }).flatten
-
end
-
-
1
def get_group_addresses(g)
-
if g.group_list.respond_to?(:addresses)
-
g.group_list.addresses
-
else
-
[]
-
end
-
end
-
end
-
end
-
# encoding: utf-8
-
1
module Mail
-
1
class ContentDispositionElement # :nodoc:
-
-
1
include Mail::Utilities
-
-
1
def initialize( string )
-
12
parser = Mail::ContentDispositionParser.new
-
12
if tree = parser.parse(cleaned(string))
-
12
@disposition_type = tree.disposition_type.text_value.downcase
-
12
@parameters = tree.parameters
-
else
-
raise Mail::Field::ParseError.new(ContentDispositionElement, string, parser.failure_reason)
-
end
-
end
-
-
1
def disposition_type
-
31
@disposition_type
-
end
-
-
1
def parameters
-
162
@parameters
-
end
-
-
1
def cleaned(string)
-
12
string =~ /(.+);\s*$/ ? $1 : string
-
end
-
-
end
-
end
-
# encoding: utf-8
-
1
module Mail
-
1
class ContentTransferEncodingElement
-
-
1
include Mail::Utilities
-
-
1
def initialize( string )
-
99
parser = Mail::ContentTransferEncodingParser.new
-
case
-
when string.blank?
-
@encoding = ''
-
when tree = parser.parse(string.to_s.downcase)
-
99
@encoding = tree.encoding.text_value
-
else
-
raise Mail::Field::ParseError.new(ContentTransferEncodingElement, string, parser.failure_reason)
-
99
end
-
end
-
-
1
def encoding
-
169
@encoding
-
end
-
-
end
-
end
-
# encoding: utf-8
-
1
module Mail
-
1
class ContentTypeElement # :nodoc:
-
-
1
include Mail::Utilities
-
-
1
def initialize( string )
-
352
parser = Mail::ContentTypeParser.new
-
352
if tree = parser.parse(cleaned(string))
-
352
@main_type = tree.main_type.text_value.downcase
-
352
@sub_type = tree.sub_type.text_value.downcase
-
352
@parameters = tree.parameters
-
else
-
raise Mail::Field::ParseError.new(ContentTypeElement, string, parser.failure_reason)
-
end
-
end
-
-
1
def main_type
-
279
@main_type
-
end
-
-
1
def sub_type
-
217
@sub_type
-
end
-
-
1
def parameters
-
275
@parameters
-
end
-
-
1
def cleaned(string)
-
352
string =~ /(.+);\s*$/ ? $1 : string
-
end
-
-
end
-
end
-
# encoding: utf-8
-
1
module Mail
-
1
class DateTimeElement # :nodoc:
-
-
1
include Mail::Utilities
-
-
1
def initialize( string )
-
7
parser = Mail::DateTimeParser.new
-
7
if tree = parser.parse(string)
-
7
@date_string = tree.date.text_value
-
7
@time_string = tree.time.text_value
-
else
-
raise Mail::Field::ParseError.new(DateTimeElement, string, parser.failure_reason)
-
end
-
end
-
-
1
def date_string
-
7
@date_string
-
end
-
-
1
def time_string
-
7
@time_string
-
end
-
-
end
-
end
-
# encoding: utf-8
-
1
module Mail
-
1
class MessageIdsElement
-
-
1
include Mail::Utilities
-
-
1
def initialize(string)
-
70
parser = Mail::MessageIdsParser.new
-
70
if tree = parser.parse(string)
-
136
@message_ids = tree.message_ids.map { |msg_id| clean_msg_id(msg_id.text_value) }
-
else
-
2
raise Mail::Field::ParseError.new(MessageIdsElement, string, parser.failure_reason)
-
end
-
end
-
-
1
def message_ids
-
@message_ids
-
end
-
-
1
def message_id
-
171
@message_ids.first
-
end
-
-
1
def clean_msg_id( val )
-
68
val =~ /.*<(.*)>.*/ ; $1
-
end
-
-
end
-
end
-
# encoding: utf-8
-
1
module Mail
-
1
class MimeVersionElement
-
-
1
include Mail::Utilities
-
-
1
def initialize( string )
-
175
parser = Mail::MimeVersionParser.new
-
175
if tree = parser.parse(string)
-
175
@major = tree.major.text_value
-
175
@minor = tree.minor.text_value
-
else
-
raise Mail::Field::ParseError.new(MimeVersionElement, string, parser.failure_reason)
-
end
-
end
-
-
1
def major
-
73
@major
-
end
-
-
1
def minor
-
73
@minor
-
end
-
-
end
-
end
-
# encoding: utf-8
-
-
1
module Mail
-
# Raised when attempting to decode an unknown encoding type
-
1
class UnknownEncodingType < StandardError #:nodoc:
-
end
-
-
1
module Encodings
-
-
1
include Mail::Patterns
-
1
extend Mail::Utilities
-
-
1
@transfer_encodings = {}
-
-
# Register transfer encoding
-
#
-
# Example
-
#
-
# Encodings.register "base64", Mail::Encodings::Base64
-
1
def Encodings.register(name, cls)
-
5
@transfer_encodings[get_name(name)] = cls
-
end
-
-
# Is the encoding we want defined?
-
#
-
# Example:
-
#
-
# Encodings.defined?(:base64) #=> true
-
1
def Encodings.defined?( str )
-
259
@transfer_encodings.include? get_name(str)
-
end
-
-
# Gets a defined encoding type, QuotedPrintable or Base64 for now.
-
#
-
# Each encoding needs to be defined as a Mail::Encodings::ClassName for
-
# this to work, allows us to add other encodings in the future.
-
#
-
# Example:
-
#
-
# Encodings.get_encoding(:base64) #=> Mail::Encodings::Base64
-
1
def Encodings.get_encoding( str )
-
887
@transfer_encodings[get_name(str)]
-
end
-
-
1
def Encodings.get_all
-
5
@transfer_encodings.values
-
end
-
-
1
def Encodings.get_name(enc)
-
1364
enc = enc.to_s.gsub("-", "_").downcase
-
end
-
-
# Encodes a parameter value using URI Escaping, note the language field 'en' can
-
# be set using Mail::Configuration, like so:
-
#
-
# Mail.defaults.do
-
# param_encode_language 'jp'
-
# end
-
#
-
# The character set used for encoding will either be the value of $KCODE for
-
# Ruby < 1.9 or the encoding on the string passed in.
-
#
-
# Example:
-
#
-
# Mail::Encodings.param_encode("This is fun") #=> "us-ascii'en'This%20is%20fun"
-
1
def Encodings.param_encode(str)
-
case
-
when str.ascii_only? && str =~ TOKEN_UNSAFE
-
29
%Q{"#{str}"}
-
when str.ascii_only?
-
73
str
-
else
-
RubyVer.param_encode(str)
-
102
end
-
end
-
-
# Decodes a parameter value using URI Escaping.
-
#
-
# Example:
-
#
-
# Mail::Encodings.param_decode("This%20is%20fun", 'us-ascii') #=> "This is fun"
-
#
-
# str = Mail::Encodings.param_decode("This%20is%20fun", 'iso-8559-1')
-
# str.encoding #=> 'ISO-8859-1' ## Only on Ruby 1.9
-
# str #=> "This is fun"
-
1
def Encodings.param_decode(str, encoding)
-
RubyVer.param_decode(str, encoding)
-
end
-
-
# Decodes or encodes a string as needed for either Base64 or QP encoding types in
-
# the =?<encoding>?[QB]?<string>?=" format.
-
#
-
# The output type needs to be :decode to decode the input string or :encode to
-
# encode the input string. The character set used for encoding will either be
-
# the value of $KCODE for Ruby < 1.9 or the encoding on the string passed in.
-
#
-
# On encoding, will only send out Base64 encoded strings.
-
1
def Encodings.decode_encode(str, output_type)
-
case
-
when output_type == :decode
-
202
Encodings.value_decode(str)
-
else
-
16
if str.ascii_only?
-
16
str
-
else
-
Encodings.b_value_encode(str, find_encoding(str))
-
end
-
218
end
-
end
-
-
# Decodes a given string as Base64 or Quoted Printable, depending on what
-
# type it is.
-
#
-
# String has to be of the format =?<encoding>?[QB]?<string>?=
-
1
def Encodings.value_decode(str)
-
# Optimization: If there's no encoded-words in the string, just return it
-
202
return str unless str.index("=?")
-
-
2
str = str.gsub(/\?=(\s*)=\?/, '?==?') # Remove whitespaces between 'encoded-word's
-
-
# Split on white-space boundaries with capture, so we capture the white-space as well
-
2
str.split(/([ \t])/).map do |text|
-
2
if text.index('=?') .nil?
-
text
-
else
-
# Join QP encoded-words that are adjacent to avoid decoding partial chars
-
2
text.gsub!(/\?\=\=\?.+?\?[Qq]\?/m, '') if text =~ /\?==\?/
-
-
# Search for occurences of quoted strings or plain strings
-
2
text.scan(/( # Group around entire regex to include it in matches
-
\=\?[^?]+\?([QB])\?[^?]+?\?\= # Quoted String with subgroup for encoding method
-
| # or
-
.+?(?=\=\?|$) # Plain String
-
)/xmi).map do |matches|
-
2
string, method = *matches
-
2
if method == 'b' || method == 'B'
-
b_value_decode(string)
-
elsif method == 'q' || method == 'Q'
-
2
q_value_decode(string)
-
else
-
string
-
end
-
end
-
end
-
end.join("")
-
end
-
-
# Takes an encoded string of the format =?<encoding>?[QB]?<string>?=
-
1
def Encodings.unquote_and_convert_to(str, to_encoding)
-
original_encoding = split_encoding_from_string( str )
-
-
output = value_decode( str ).to_s
-
-
if original_encoding.to_s.downcase.gsub("-", "") == to_encoding.to_s.downcase.gsub("-", "")
-
output
-
elsif original_encoding && to_encoding
-
begin
-
if RUBY_VERSION >= '1.9'
-
output.encode(to_encoding)
-
else
-
require 'iconv'
-
Iconv.iconv(to_encoding, original_encoding, output).first
-
end
-
rescue Iconv::IllegalSequence, Iconv::InvalidEncoding, Errno::EINVAL
-
# the 'from' parameter specifies a charset other than what the text
-
# actually is...not much we can do in this case but just return the
-
# unconverted text.
-
#
-
# Ditto if either parameter represents an unknown charset, like
-
# X-UNKNOWN.
-
output
-
end
-
else
-
output
-
end
-
end
-
-
1
def Encodings.address_encode(address, charset = 'utf-8')
-
289
if address.is_a?(Array)
-
# loop back through for each element
-
address.map { |a| Encodings.address_encode(a, charset) }.join(", ")
-
else
-
# find any word boundary that is not ascii and encode it
-
289
encode_non_usascii(address, charset)
-
end
-
end
-
-
1
def Encodings.encode_non_usascii(address, charset)
-
289
return address if address.ascii_only? or charset.nil?
-
us_ascii = %Q{\x00-\x7f}
-
# Encode any non usascii strings embedded inside of quotes
-
address.gsub!(/(".*?[^#{us_ascii}].*?")/) { |s| Encodings.b_value_encode(unquote(s), charset) }
-
# Then loop through all remaining items and encode as needed
-
tokens = address.split(/\s/)
-
map_with_index(tokens) do |word, i|
-
if word.ascii_only?
-
word
-
else
-
previous_non_ascii = tokens[i-1] && !tokens[i-1].ascii_only?
-
if previous_non_ascii
-
word = " #{word}"
-
end
-
Encodings.b_value_encode(word, charset)
-
end
-
end.join(' ')
-
end
-
-
# Encode a string with Base64 Encoding and returns it ready to be inserted
-
# as a value for a field, that is, in the =?<charset>?B?<string>?= format
-
#
-
# Example:
-
#
-
# Encodings.b_value_encode('This is あ string', 'UTF-8')
-
# #=> "=?UTF-8?B?VGhpcyBpcyDjgYIgc3RyaW5n?="
-
1
def Encodings.b_value_encode(encoded_str, encoding = nil)
-
return encoded_str if encoded_str.to_s.ascii_only?
-
string, encoding = RubyVer.b_value_encode(encoded_str, encoding)
-
map_lines(string) do |str|
-
"=?#{encoding}?B?#{str.chomp}?="
-
end.join(" ")
-
end
-
-
# Encode a string with Quoted-Printable Encoding and returns it ready to be inserted
-
# as a value for a field, that is, in the =?<charset>?Q?<string>?= format
-
#
-
# Example:
-
#
-
# Encodings.q_value_encode('This is あ string', 'UTF-8')
-
# #=> "=?UTF-8?Q?This_is_=E3=81=82_string?="
-
1
def Encodings.q_value_encode(encoded_str, encoding = nil)
-
return encoded_str if encoded_str.to_s.ascii_only?
-
string, encoding = RubyVer.q_value_encode(encoded_str, encoding)
-
string.gsub!("=\r\n", '') # We already have limited the string to the length we want
-
map_lines(string) do |str|
-
"=?#{encoding}?Q?#{str.chomp.gsub(/ /, '_')}?="
-
end.join(" ")
-
end
-
-
1
private
-
-
# Decodes a Base64 string from the "=?UTF-8?B?VGhpcyBpcyDjgYIgc3RyaW5n?=" format
-
#
-
# Example:
-
#
-
# Encodings.b_value_decode("=?UTF-8?B?VGhpcyBpcyDjgYIgc3RyaW5n?=")
-
# #=> 'This is あ string'
-
1
def Encodings.b_value_decode(str)
-
RubyVer.b_value_decode(str)
-
end
-
-
# Decodes a Quoted-Printable string from the "=?UTF-8?Q?This_is_=E3=81=82_string?=" format
-
#
-
# Example:
-
#
-
# Encodings.q_value_decode("=?UTF-8?Q?This_is_=E3=81=82_string?=")
-
# #=> 'This is あ string'
-
1
def Encodings.q_value_decode(str)
-
2
RubyVer.q_value_decode(str)
-
end
-
-
1
def Encodings.split_encoding_from_string( str )
-
match = str.match(/\=\?([^?]+)?\?[QB]\?(.+)?\?\=/mi)
-
if match
-
match[1]
-
else
-
nil
-
end
-
end
-
-
1
def Encodings.find_encoding(str)
-
RUBY_VERSION >= '1.9' ? str.encoding : $KCODE
-
end
-
end
-
end
-
# encoding: utf-8
-
1
require 'mail/encodings/8bit'
-
-
1
module Mail
-
1
module Encodings
-
1
class SevenBit < EightBit
-
1
NAME = '7bit'
-
-
1
PRIORITY = 1
-
-
# 7bit and 8bit operate the same
-
-
# Decode the string
-
1
def self.decode(str)
-
139
super
-
end
-
-
# Encode the string
-
1
def self.encode(str)
-
97
super
-
end
-
-
# Idenity encodings have a fixed cost, 1 byte out per 1 byte in
-
1
def self.cost(str)
-
super
-
end
-
-
1
Encodings.register(NAME, self)
-
end
-
end
-
end
-
# encoding: utf-8
-
1
require 'mail/encodings/binary'
-
-
1
module Mail
-
1
module Encodings
-
1
class EightBit < Binary
-
1
NAME = '8bit'
-
-
1
PRIORITY = 4
-
-
# 8bit is an identiy encoding, meaning nothing to do
-
-
# Decode the string
-
1
def self.decode(str)
-
139
str.to_lf
-
end
-
-
# Encode the string
-
1
def self.encode(str)
-
97
str.to_crlf
-
end
-
-
# Idenity encodings have a fixed cost, 1 byte out per 1 byte in
-
1
def self.cost(str)
-
1.0
-
end
-
-
1
Encodings.register(NAME, self)
-
end
-
end
-
end
-
# encoding: utf-8
-
1
require 'mail/encodings/7bit'
-
-
1
module Mail
-
1
module Encodings
-
1
class Base64 < SevenBit
-
1
NAME = 'base64'
-
-
1
PRIORITY = 3
-
-
1
def self.can_encode?(enc)
-
5
true
-
end
-
-
# Decode the string from Base64
-
1
def self.decode(str)
-
1
RubyVer.decode_base64( str )
-
end
-
-
# Encode the string to Base64
-
1
def self.encode(str)
-
4
RubyVer.encode_base64( str ).to_crlf
-
end
-
-
# Base64 has a fixed cost, 4 bytes out per 3 bytes in
-
1
def self.cost(str)
-
5
4.0/3
-
end
-
-
1
Encodings.register(NAME, self)
-
end
-
end
-
end
-
# encoding: utf-8
-
1
require 'mail/encodings/transfer_encoding'
-
-
1
module Mail
-
1
module Encodings
-
1
class Binary < TransferEncoding
-
1
NAME = 'binary'
-
-
1
PRIORITY = 5
-
-
# Binary is an identiy encoding, meaning nothing to do
-
-
# Decode the string
-
1
def self.decode(str)
-
6
str
-
end
-
-
# Encode the string
-
1
def self.encode(str)
-
str
-
end
-
-
# Idenity encodings have a fixed cost, 1 byte out per 1 byte in
-
1
def self.cost(str)
-
1.0
-
end
-
-
1
Encodings.register(NAME, self)
-
end
-
end
-
end
-
# encoding: utf-8
-
1
require 'mail/encodings/7bit'
-
-
1
module Mail
-
1
module Encodings
-
1
class QuotedPrintable < SevenBit
-
1
NAME='quoted-printable'
-
-
1
PRIORITY = 2
-
-
1
def self.can_encode?(str)
-
4
EightBit.can_encode? str
-
end
-
-
# Decode the string from Quoted-Printable
-
1
def self.decode(str)
-
2
str.unpack("M*").first
-
end
-
-
1
def self.encode(str)
-
[str].pack("M").gsub(/\n/, "\r\n")
-
end
-
-
1
def self.cost(str)
-
# These bytes probably do not need encoding
-
c = str.count("\x9\xA\xD\x20-\x3C\x3E-\x7E")
-
# Everything else turns into =XX where XX is a
-
# two digit hex number (taking 3 bytes)
-
total = (str.bytesize - c)*3 + c
-
total.to_f/str.bytesize
-
end
-
-
1
private
-
-
1
Encodings.register(NAME, self)
-
end
-
end
-
end
-
# encoding: utf-8
-
1
module Mail
-
1
module Encodings
-
1
class TransferEncoding
-
1
NAME = ''
-
-
1
PRIORITY = -1
-
-
1
def self.can_transport?(enc)
-
213
enc = Encodings.get_name(enc)
-
213
if Encodings.defined? enc
-
213
Encodings.get_encoding(enc).new.is_a? self
-
else
-
false
-
end
-
end
-
-
1
def self.can_encode?(enc)
-
10
can_transport? enc
-
end
-
-
1
def self.cost(str)
-
raise "Unimplemented"
-
end
-
-
1
def self.to_s
-
146
self::NAME
-
end
-
-
1
def self.get_best_compatible(source_encoding, str)
-
178
if self.can_transport? source_encoding then
-
173
source_encoding
-
else
-
5
choices = []
-
5
Encodings.get_all.each do |enc|
-
25
choices << enc if self.can_transport? enc and enc.can_encode? source_encoding
-
end
-
5
best = nil
-
5
best_cost = 100
-
5
choices.each do |enc|
-
5
this_cost = enc.cost str
-
5
if this_cost < best_cost then
-
5
best_cost = this_cost
-
5
best = enc
-
elsif this_cost == best_cost then
-
best = enc if enc::PRIORITY < best::PRIORITY
-
end
-
end
-
5
best
-
end
-
end
-
-
1
def to_s
-
self.class.to_s
-
end
-
end
-
end
-
end
-
# encoding: utf-8
-
#
-
# = Mail Envelope
-
#
-
# The Envelope class provides a field for the first line in an
-
# mbox file, that looks like "From mikel@test.lindsaar.net DATETIME"
-
#
-
# This envelope class reads that line, and turns it into an
-
# Envelope.from and Envelope.date for your use.
-
1
module Mail
-
1
class Envelope < StructuredField
-
-
1
def initialize(*args)
-
1
super(FIELD_NAME, strip_field(FIELD_NAME, args.last))
-
end
-
-
1
def tree
-
@element ||= Mail::EnvelopeFromElement.new(value)
-
@tree ||= @element.tree
-
end
-
-
1
def element
-
@element ||= Mail::EnvelopeFromElement.new(value)
-
end
-
-
1
def date
-
::DateTime.parse("#{element.date_time}")
-
end
-
-
1
def from
-
element.address
-
end
-
-
end
-
end
-
1
require 'mail/fields'
-
-
# encoding: utf-8
-
1
module Mail
-
# Provides a single class to call to create a new structured or unstructured
-
# field. Works out per RFC what field of field it is being given and returns
-
# the correct field of class back on new.
-
#
-
# ===Per RFC 2822
-
#
-
# 2.2. Header Fields
-
#
-
# Header fields are lines composed of a field name, followed by a colon
-
# (":"), followed by a field body, and terminated by CRLF. A field
-
# name MUST be composed of printable US-ASCII characters (i.e.,
-
# characters that have values between 33 and 126, inclusive), except
-
# colon. A field body may be composed of any US-ASCII characters,
-
# except for CR and LF. However, a field body may contain CRLF when
-
# used in header "folding" and "unfolding" as described in section
-
# 2.2.3. All field bodies MUST conform to the syntax described in
-
# sections 3 and 4 of this standard.
-
#
-
1
class Field
-
-
1
include Patterns
-
1
include Comparable
-
-
1
STRUCTURED_FIELDS = %w[ bcc cc content-description content-disposition
-
content-id content-location content-transfer-encoding
-
content-type date from in-reply-to keywords message-id
-
mime-version received references reply-to
-
resent-bcc resent-cc resent-date resent-from
-
resent-message-id resent-sender resent-to
-
return-path sender to ]
-
-
1
KNOWN_FIELDS = STRUCTURED_FIELDS + ['comments', 'subject']
-
-
# Generic Field Exception
-
1
class FieldError < StandardError
-
end
-
-
# Raised when a parsing error has occurred (ie, a StructuredField has tried
-
# to parse a field that is invalid or improperly written)
-
1
class ParseError < FieldError #:nodoc:
-
1
attr_accessor :element, :value, :reason
-
-
1
def initialize(element, value, reason)
-
2
@element = element
-
2
@value = value
-
2
@reason = reason
-
2
super("#{element} can not parse |#{value}|\nReason was: #{reason}")
-
end
-
end
-
-
# Raised when attempting to set a structured field's contents to an invalid syntax
-
1
class SyntaxError < FieldError #:nodoc:
-
end
-
-
# Accepts a string:
-
#
-
# Field.new("field-name: field data")
-
#
-
# Or name, value pair:
-
#
-
# Field.new("field-name", "value")
-
#
-
# Or a name by itself:
-
#
-
# Field.new("field-name")
-
#
-
# Note, does not want a terminating carriage return. Returns
-
# self appropriately parsed. If value is not a string, then
-
# it will be passed through as is, for example, content-type
-
# field can accept an array with the type and a hash of
-
# parameters:
-
#
-
# Field.new('content-type', ['text', 'plain', {:charset => 'UTF-8'}])
-
1
def initialize(name, value = nil, charset = 'utf-8')
-
case
-
when name =~ /:/ # Field.new("field-name: field data")
-
12
charset = value unless value.blank?
-
12
name, value = split(name)
-
12
create_field(name, value, charset)
-
when name !~ /:/ && value.blank? # Field.new("field-name")
-
118
create_field(name, nil, charset)
-
else # Field.new("field-name", "value")
-
918
create_field(name, value, charset)
-
1048
end
-
1048
return self
-
end
-
-
1
def field=(value)
-
1208
@field = value
-
end
-
-
1
def field
-
46660
@field
-
end
-
-
1
def name
-
6728
field.name
-
end
-
-
1
def value
-
field.value
-
end
-
-
1
def value=(val)
-
create_field(name, val, charset)
-
end
-
-
1
def to_s
-
5
field.to_s
-
end
-
-
1
def update(name, value)
-
160
create_field(name, value, charset)
-
end
-
-
1
def same( other )
-
2758
match_to_s(other.name, field.name)
-
end
-
-
1
alias_method :==, :same
-
-
1
def <=>( other )
-
1979
self_order = FIELD_ORDER.rindex(self.name.to_s.downcase) || 100
-
1979
other_order = FIELD_ORDER.rindex(other.name.to_s.downcase) || 100
-
1979
self_order <=> other_order
-
end
-
-
1
def method_missing(name, *args, &block)
-
37165
field.send(name, *args, &block)
-
end
-
-
1
FIELD_ORDER = %w[ return-path received
-
resent-date resent-from resent-sender resent-to
-
resent-cc resent-bcc resent-message-id
-
date from sender reply-to to cc bcc
-
message-id in-reply-to references
-
subject comments keywords
-
mime-version content-type content-transfer-encoding
-
content-location content-disposition content-description ]
-
-
1
private
-
-
1
def split(raw_field)
-
12
match_data = raw_field.mb_chars.match(/^(#{FIELD_NAME})\s*:\s*(#{FIELD_BODY})?$/)
-
12
[match_data[1].to_s.mb_chars.strip, match_data[2].to_s.mb_chars.strip]
-
rescue
-
STDERR.puts "WARNING: Could not parse (and so ignorning) '#{raw_field}'"
-
end
-
-
1
def create_field(name, value, charset)
-
1208
begin
-
1208
self.field = new_field(name, value, charset)
-
2
rescue Mail::Field::ParseError => e
-
2
self.field = Mail::UnstructuredField.new(name, value)
-
2
self.field.errors << [name, value, e]
-
2
self.field
-
end
-
end
-
-
1
def new_field(name, value, charset)
-
# Could do this with constantize and make it "as DRY as", but a simple case
-
# statement is, well, simpler...
-
1208
case name.to_s.downcase
-
when /^to$/i
-
113
ToField.new(value, charset)
-
when /^cc$/i
-
1
CcField.new(value, charset)
-
when /^bcc$/i
-
1
BccField.new(value, charset)
-
when /^message-id$/i
-
45
MessageIdField.new(value, charset)
-
when /^in-reply-to$/i
-
2
InReplyToField.new(value, charset)
-
when /^references$/i
-
ReferencesField.new(value, charset)
-
when /^subject$/i
-
120
SubjectField.new(value, charset)
-
when /^comments$/i
-
CommentsField.new(value, charset)
-
when /^keywords$/i
-
KeywordsField.new(value, charset)
-
when /^date$/i
-
54
DateField.new(value, charset)
-
when /^from$/i
-
110
FromField.new(value, charset)
-
when /^sender$/i
-
SenderField.new(value, charset)
-
when /^reply-to$/i
-
64
ReplyToField.new(value, charset)
-
when /^resent-date$/i
-
ResentDateField.new(value, charset)
-
when /^resent-from$/i
-
ResentFromField.new(value, charset)
-
when /^resent-sender$/i
-
ResentSenderField.new(value, charset)
-
when /^resent-to$/i
-
ResentToField.new(value, charset)
-
when /^resent-cc$/i
-
ResentCcField.new(value, charset)
-
when /^resent-bcc$/i
-
ResentBccField.new(value, charset)
-
when /^resent-message-id$/i
-
ResentMessageIdField.new(value, charset)
-
when /^return-path$/i
-
ReturnPathField.new(value, charset)
-
when /^received$/i
-
ReceivedField.new(value, charset)
-
when /^mime-version$/i
-
175
MimeVersionField.new(value, charset)
-
when /^content-transfer-encoding$/i
-
99
ContentTransferEncodingField.new(value, charset)
-
when /^content-description$/i
-
ContentDescriptionField.new(value, charset)
-
when /^content-disposition$/i
-
12
ContentDispositionField.new(value, charset)
-
when /^content-type$/i
-
352
ContentTypeField.new(value, charset)
-
when /^content-id$/i
-
23
ContentIdField.new(value, charset)
-
when /^content-location$/i
-
ContentLocationField.new(value, charset)
-
else
-
37
OptionalField.new(name, value, charset)
-
end
-
-
end
-
-
end
-
-
end
-
# encoding: utf-8
-
1
module Mail
-
-
# Field List class provides an enhanced array that keeps a list of
-
# email fields in order. And allows you to insert new fields without
-
# having to worry about the order they will appear in.
-
1
class FieldList < Array
-
-
1
include Enumerable
-
-
1
def <<( new_field )
-
1048
current_entry = self.rindex(new_field)
-
1048
if current_entry
-
self.insert((current_entry + 1), new_field)
-
else
-
1048
insert_idx = -1
-
1048
self.each_with_index do |item, idx|
-
1979
case item <=> new_field
-
when -1
-
1339
next
-
when 0
-
2
next
-
when 1
-
638
insert_idx = idx
-
638
break
-
end
-
end
-
1048
insert(insert_idx, new_field)
-
end
-
end
-
-
end
-
end
-
1
module Mail
-
1
autoload :UnstructuredField, 'mail/fields/unstructured_field'
-
1
autoload :StructuredField, 'mail/fields/structured_field'
-
1
autoload :OptionalField, 'mail/fields/optional_field'
-
-
1
autoload :BccField, 'mail/fields/bcc_field'
-
1
autoload :CcField, 'mail/fields/cc_field'
-
1
autoload :CommentsField, 'mail/fields/comments_field'
-
1
autoload :ContentDescriptionField, 'mail/fields/content_description_field'
-
1
autoload :ContentDispositionField, 'mail/fields/content_disposition_field'
-
1
autoload :ContentIdField, 'mail/fields/content_id_field'
-
1
autoload :ContentLocationField, 'mail/fields/content_location_field'
-
1
autoload :ContentTransferEncodingField, 'mail/fields/content_transfer_encoding_field'
-
1
autoload :ContentTypeField, 'mail/fields/content_type_field'
-
1
autoload :DateField, 'mail/fields/date_field'
-
1
autoload :FromField, 'mail/fields/from_field'
-
1
autoload :InReplyToField, 'mail/fields/in_reply_to_field'
-
1
autoload :KeywordsField, 'mail/fields/keywords_field'
-
1
autoload :MessageIdField, 'mail/fields/message_id_field'
-
1
autoload :MimeVersionField, 'mail/fields/mime_version_field'
-
1
autoload :ReceivedField, 'mail/fields/received_field'
-
1
autoload :ReferencesField, 'mail/fields/references_field'
-
1
autoload :ReplyToField, 'mail/fields/reply_to_field'
-
1
autoload :ResentBccField, 'mail/fields/resent_bcc_field'
-
1
autoload :ResentCcField, 'mail/fields/resent_cc_field'
-
1
autoload :ResentDateField, 'mail/fields/resent_date_field'
-
1
autoload :ResentFromField, 'mail/fields/resent_from_field'
-
1
autoload :ResentMessageIdField, 'mail/fields/resent_message_id_field'
-
1
autoload :ResentSenderField, 'mail/fields/resent_sender_field'
-
1
autoload :ResentToField, 'mail/fields/resent_to_field'
-
1
autoload :ReturnPathField, 'mail/fields/return_path_field'
-
1
autoload :SenderField, 'mail/fields/sender_field'
-
1
autoload :SubjectField, 'mail/fields/subject_field'
-
1
autoload :ToField, 'mail/fields/to_field'
-
end
-
# encoding: utf-8
-
#
-
# = Blind Carbon Copy Field
-
#
-
# The Bcc field inherits from StructuredField and handles the Bcc: header
-
# field in the email.
-
#
-
# Sending bcc to a mail message will instantiate a Mail::Field object that
-
# has a BccField as it's field type. This includes all Mail::CommonAddress
-
# module instance metods.
-
#
-
# Only one Bcc field can appear in a header, though it can have multiple
-
# addresses and groups of addresses.
-
#
-
# == Examples:
-
#
-
# mail = Mail.new
-
# mail.bcc = 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
-
# mail.bcc #=> ['Mikel Lindsaar <mikel@test.lindsaar.net>', 'ada@test.lindsaar.net']
-
# mail[:bcc] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::BccField:0x180e1c4
-
# mail['bcc'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::BccField:0x180e1c4
-
# mail['Bcc'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::BccField:0x180e1c4
-
#
-
# mail[:bcc].encoded #=> '' # Bcc field does not get output into an email
-
# mail[:bcc].decoded #=> 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
-
# mail[:bcc].addresses #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
# mail[:bcc].formatted #=> ['Mikel Lindsaar <mikel@test.lindsaar.net>', 'ada@test.lindsaar.net']
-
#
-
1
require 'mail/fields/common/common_address'
-
-
1
module Mail
-
1
class BccField < StructuredField
-
-
1
include Mail::CommonAddress
-
-
1
FIELD_NAME = 'bcc'
-
1
CAPITALIZED_FIELD = 'Bcc'
-
-
1
def initialize(value = '', charset = 'utf-8')
-
1
@charset = charset
-
1
super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
-
1
self.parse
-
1
self
-
end
-
-
# Bcc field should never be :encoded
-
1
def encoded
-
''
-
end
-
-
1
def decoded
-
do_decode
-
end
-
-
end
-
end
-
# encoding: utf-8
-
#
-
# = Carbon Copy Field
-
#
-
# The Cc field inherits from StructuredField and handles the Cc: header
-
# field in the email.
-
#
-
# Sending cc to a mail message will instantiate a Mail::Field object that
-
# has a CcField as it's field type. This includes all Mail::CommonAddress
-
# module instance metods.
-
#
-
# Only one Cc field can appear in a header, though it can have multiple
-
# addresses and groups of addresses.
-
#
-
# == Examples:
-
#
-
# mail = Mail.new
-
# mail.cc = 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
-
# mail.cc #=> ['Mikel Lindsaar <mikel@test.lindsaar.net>', 'ada@test.lindsaar.net']
-
# mail[:cc] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::CcField:0x180e1c4
-
# mail['cc'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::CcField:0x180e1c4
-
# mail['Cc'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::CcField:0x180e1c4
-
#
-
# mail[:cc].encoded #=> 'Cc: Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net\r\n'
-
# mail[:cc].decoded #=> 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
-
# mail[:cc].addresses #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
# mail[:cc].formatted #=> ['Mikel Lindsaar <mikel@test.lindsaar.net>', 'ada@test.lindsaar.net']
-
#
-
1
require 'mail/fields/common/common_address'
-
-
1
module Mail
-
1
class CcField < StructuredField
-
-
1
include Mail::CommonAddress
-
-
1
FIELD_NAME = 'cc'
-
1
CAPITALIZED_FIELD = 'Cc'
-
-
1
def initialize(value = nil, charset = 'utf-8')
-
1
self.charset = charset
-
1
super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
-
1
self.parse
-
1
self
-
end
-
-
1
def encoded
-
do_encode(CAPITALIZED_FIELD)
-
end
-
-
1
def decoded
-
do_decode
-
end
-
-
end
-
end
-
1
module Mail
-
-
1
class AddressContainer < Array
-
-
1
def initialize(field, list = [])
-
88
@field = field
-
88
super(list)
-
end
-
-
1
def << (address)
-
@field << address
-
end
-
-
end
-
-
end
-
# encoding: utf-8
-
1
require 'mail/fields/common/address_container'
-
-
1
module Mail
-
1
module CommonAddress # :nodoc:
-
-
1
def parse(val = value)
-
289
unless val.blank?
-
289
@tree = AddressList.new(encode_if_needed(val))
-
else
-
nil
-
end
-
end
-
-
1
def charset
-
289
@charset
-
end
-
-
1
def encode_if_needed(val)
-
289
Encodings.address_encode(val, charset)
-
end
-
-
# Allows you to iterate through each address object in the syntax tree
-
1
def each
-
tree.addresses.each do |address|
-
yield(address)
-
end
-
end
-
-
# Returns the address string of all the addresses in the address list
-
1
def addresses
-
176
list = tree.addresses.map { |a| a.address }
-
88
Mail::AddressContainer.new(self, list)
-
end
-
-
# Returns the formatted string of all the addresses in the address list
-
1
def formatted
-
list = tree.addresses.map { |a| a.format }
-
Mail::AddressContainer.new(self, list)
-
end
-
-
# Returns the display name of all the addresses in the address list
-
1
def display_names
-
list = tree.addresses.map { |a| a.display_name }
-
Mail::AddressContainer.new(self, list)
-
end
-
-
# Returns the actual address objects in the address list
-
1
def addrs
-
list = tree.addresses
-
Mail::AddressContainer.new(self, list)
-
end
-
-
# Returns a hash of group name => address strings for the address list
-
1
def groups
-
298
@groups = Hash.new
-
298
tree.group_recipients.each do |group|
-
@groups[group.group_name.text_value.to_str] = get_group_addresses(group.group_list)
-
end
-
298
@groups
-
end
-
-
# Returns the addresses that are part of groups
-
1
def group_addresses
-
149
groups.map { |k,v| v.map { |a| a.format } }.flatten
-
end
-
-
# Returns the name of all the groups in a string
-
1
def group_names # :nodoc:
-
tree.group_names
-
end
-
-
1
def default
-
88
addresses
-
end
-
-
1
def <<(val)
-
case
-
when val.nil?
-
raise ArgumentError, "Need to pass an address to <<"
-
when val.blank?
-
parse(encoded)
-
else
-
parse((formatted + [val]).join(", "))
-
end
-
end
-
-
1
private
-
-
1
def do_encode(field_name)
-
149
return '' if value.blank?
-
447
address_array = tree.addresses.reject { |a| group_addresses.include?(a.encoded) }.compact.map { |a| a.encoded }
-
149
address_text = address_array.join(", \r\n\s")
-
149
group_array = groups.map { |k,v| "#{k}: #{v.map { |a| a.encoded }.join(", \r\n\s")};" }
-
149
group_text = group_array.join(" \r\n\s")
-
447
return_array = [address_text, group_text].reject { |a| a.blank? }
-
149
"#{field_name}: #{return_array.join(", \r\n\s")}\r\n"
-
end
-
-
1
def do_decode
-
return nil if value.blank?
-
address_array = tree.addresses.reject { |a| group_addresses.include?(a.decoded) }.map { |a| a.decoded }
-
address_text = address_array.join(", ")
-
group_array = groups.map { |k,v| "#{k}: #{v.map { |a| a.decoded }.join(", ")};" }
-
group_text = group_array.join(" ")
-
return_array = [address_text, group_text].reject { |a| a.blank? }
-
return_array.join(", ")
-
end
-
-
# Returns the syntax tree of the Addresses
-
1
def tree # :nodoc:
-
535
@tree ||= AddressList.new(value)
-
end
-
-
1
def get_group_addresses(group_list)
-
if group_list.respond_to?(:addresses)
-
group_list.addresses.map do |address_tree|
-
Mail::Address.new(address_tree)
-
end
-
else
-
[]
-
end
-
end
-
end
-
end
-
# encoding: utf-8
-
1
module Mail
-
1
module CommonDate # :nodoc:
-
# Returns a date time object of the parsed date
-
1
def date_time
-
7
::DateTime.parse("#{element.date_string} #{element.time_string}")
-
end
-
-
1
def default
-
7
date_time
-
end
-
-
1
def parse(val = value)
-
unless val.blank?
-
@element = Mail::DateTimeElement.new(val)
-
@tree = @element.tree
-
else
-
nil
-
end
-
end
-
-
1
private
-
-
1
def do_encode(field_name)
-
69
"#{field_name}: #{value}\r\n"
-
end
-
-
1
def do_decode
-
"#{value}"
-
end
-
-
1
def element
-
14
@element ||= Mail::DateTimeElement.new(value)
-
end
-
-
# Returns the syntax tree of the Date
-
1
def tree
-
@tree ||= element.tree
-
end
-
-
end
-
end
-
# encoding: utf-8
-
1
module Mail
-
1
module CommonField # :nodoc:
-
-
1
def name=(value)
-
1211
@name = value
-
end
-
-
1
def name
-
37749
@name ||= nil
-
end
-
-
1
def value=(value)
-
1563
@length = nil
-
1563
@tree = nil
-
1563
@element = nil
-
1563
@value = value
-
end
-
-
1
def value
-
1694
@value
-
end
-
-
1
def to_s
-
5
decoded
-
end
-
-
1
def default
-
decoded
-
end
-
-
1
def field_length
-
@length ||= "#{name}: #{encode(decoded)}".length
-
end
-
-
1
def responsible_for?( val )
-
30401
name.to_s.downcase == val.to_s.downcase
-
end
-
-
1
private
-
-
1
def strip_field(field_name, value)
-
1016
if value.is_a?(Array)
-
value
-
else
-
1016
value.to_s.gsub(/#{field_name}:\s+/i, '')
-
end
-
end
-
-
end
-
end
-
# encoding: utf-8
-
1
module Mail
-
1
module CommonMessageId # :nodoc:
-
1
def element
-
320
@element ||= Mail::MessageIdsElement.new(value) unless value.blank?
-
end
-
-
1
def parse(val = value)
-
47
unless val.blank?
-
47
@element = Mail::MessageIdsElement.new(val)
-
else
-
nil
-
end
-
end
-
-
1
def message_id
-
160
element.message_id if element
-
end
-
-
1
def message_ids
-
element.message_ids if element
-
end
-
-
1
def default
-
14
return nil unless message_ids
-
14
if message_ids.length == 1
-
14
message_ids[0]
-
else
-
message_ids
-
end
-
end
-
-
1
private
-
-
1
def do_encode(field_name)
-
59
%Q{#{field_name}: #{do_decode}\r\n}
-
end
-
-
1
def do_decode
-
118
"#{message_ids.map { |m| "<#{m}>" }.join(' ')}" if message_ids
-
end
-
-
end
-
end
-
# encoding: utf-8
-
1
module Mail
-
-
# ParameterHash is an intelligent Hash that allows you to add
-
# parameter values including the MIME extension paramaters that
-
# have the name*0="blah", name*1="bleh" keys, and will just return
-
# a single key called name="blahbleh" and do any required un-encoding
-
# to make that happen
-
# Parameters are defined in RFC2045, split keys are in RFC2231
-
-
1
class ParameterHash < IndifferentHash
-
-
1
include Mail::Utilities
-
-
1
def [](key_name)
-
827
key_pattern = Regexp.escape(key_name.to_s)
-
827
pairs = []
-
827
exact = nil
-
827
each do |k,v|
-
891
if k =~ /^#{key_pattern}(\*|$)/i
-
581
if $1 == '*'
-
pairs << [k, v]
-
else
-
581
exact = k
-
end
-
end
-
end
-
827
if pairs.empty? # Just dealing with a single value pair
-
827
super(exact || key_name)
-
else # Dealing with a multiple value pair or a single encoded value pair
-
string = pairs.sort { |a,b| a.first.to_s <=> b.first.to_s }.map { |v| v.last }.join('')
-
if mt = string.match(/([\w\-]+)'(\w\w)'(.*)/)
-
string = mt[3]
-
encoding = mt[1]
-
else
-
encoding = nil
-
end
-
Mail::Encodings.param_decode(string, encoding)
-
end
-
end
-
-
1
def encoded
-
77
map.sort { |a,b| a.first.to_s <=> b.first.to_s }.map do |key_name, value|
-
77
unless value.ascii_only?
-
value = Mail::Encodings.param_encode(value)
-
key_name = "#{key_name}*"
-
end
-
77
%Q{#{key_name}=#{quote_token(value)}}
-
end.join(";\r\n\s")
-
end
-
-
1
def decoded
-
413
map.sort { |a,b| a.first.to_s <=> b.first.to_s }.map do |key_name, value|
-
413
%Q{#{key_name}=#{quote_token(value)}}
-
end.join("; ")
-
end
-
end
-
end
-
# encoding: utf-8
-
1
require 'mail/fields/common/parameter_hash'
-
-
1
module Mail
-
1
class ContentDispositionField < StructuredField
-
-
1
FIELD_NAME = 'content-disposition'
-
1
CAPITALIZED_FIELD = 'Content-Disposition'
-
-
1
def initialize(value = nil, charset = 'utf-8')
-
12
self.charset = charset
-
12
super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
-
12
self.parse
-
12
self
-
end
-
-
1
def parse(val = value)
-
12
unless val.blank?
-
12
@element = Mail::ContentDispositionElement.new(val)
-
end
-
end
-
-
1
def element
-
193
@element ||= Mail::ContentDispositionElement.new(value)
-
end
-
-
1
def disposition_type
-
31
element.disposition_type
-
end
-
-
1
def parameters
-
162
@parameters = ParameterHash.new
-
324
element.parameters.each { |p| @parameters.merge!(p) }
-
162
@parameters
-
end
-
-
1
def filename
-
case
-
when !parameters['filename'].blank?
-
62
@filename = parameters['filename']
-
when !parameters['name'].blank?
-
@filename = parameters['name']
-
else
-
@filename = nil
-
62
end
-
62
@filename
-
end
-
-
# TODO: Fix this up
-
1
def encoded
-
1
if parameters.length > 0
-
1
p = ";\r\n\s#{parameters.encoded}\r\n"
-
else
-
p = "\r\n"
-
end
-
1
"#{CAPITALIZED_FIELD}: #{disposition_type}" + p
-
end
-
-
1
def decoded
-
18
if parameters.length > 0
-
18
p = "; #{parameters.decoded}"
-
else
-
p = ""
-
end
-
18
"#{disposition_type}" + p
-
end
-
-
end
-
end
-
# encoding: utf-8
-
#
-
#
-
#
-
1
module Mail
-
1
class ContentIdField < StructuredField
-
-
1
FIELD_NAME = 'content-id'
-
1
CAPITALIZED_FIELD = "Content-ID"
-
-
1
def initialize(value = nil, charset = 'utf-8')
-
23
self.charset = charset
-
23
@uniq = 1
-
23
if value.blank?
-
23
value = generate_content_id
-
else
-
value = strip_field(FIELD_NAME, value)
-
end
-
23
super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
-
23
self.parse
-
23
self
-
end
-
-
1
def parse(val = value)
-
23
unless val.blank?
-
23
@element = Mail::MessageIdsElement.new(val)
-
end
-
end
-
-
1
def element
-
11
@element ||= Mail::MessageIdsElement.new(value)
-
end
-
-
1
def name
-
1064
'Content-ID'
-
end
-
-
1
def content_id
-
11
element.message_id
-
end
-
-
1
def to_s
-
11
"<#{content_id}>"
-
end
-
-
# TODO: Fix this up
-
1
def encoded
-
10
"#{CAPITALIZED_FIELD}: #{to_s}\r\n"
-
end
-
-
1
def decoded
-
1
"#{to_s}"
-
end
-
-
1
private
-
-
1
def generate_content_id
-
23
fqdn = ::Socket.gethostname
-
23
"<#{Mail.random_tag}@#{fqdn}.mail>"
-
end
-
-
end
-
end
-
# encoding: utf-8
-
#
-
#
-
#
-
1
module Mail
-
1
class ContentTransferEncodingField < StructuredField
-
-
1
FIELD_NAME = 'content-transfer-encoding'
-
1
CAPITALIZED_FIELD = 'Content-Transfer-Encoding'
-
-
1
def initialize(value = nil, charset = 'utf-8')
-
99
self.charset = charset
-
99
value = '7bit' if value.to_s =~ /7-bit/i
-
99
value = '8bit' if value.to_s =~ /8-bit/i
-
99
super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
-
99
self.parse
-
99
self
-
end
-
-
1
def parse(val = value)
-
99
unless val.blank?
-
99
@element = Mail::ContentTransferEncodingElement.new(val)
-
end
-
end
-
-
1
def tree
-
STDERR.puts("tree is deprecated. Please use encoding to get parse result\n#{caller}")
-
@element ||= Mail::ContentTransferEncodingElement.new(value)
-
@tree ||= @element.tree
-
end
-
-
1
def element
-
169
@element ||= Mail::ContentTransferEncodingElement.new(value)
-
end
-
-
1
def encoding
-
169
element.encoding
-
end
-
-
# TODO: Fix this up
-
1
def encoded
-
69
"#{CAPITALIZED_FIELD}: #{encoding}\r\n"
-
end
-
-
1
def decoded
-
100
encoding
-
end
-
-
end
-
end
-
# encoding: utf-8
-
1
require 'mail/fields/common/parameter_hash'
-
-
1
module Mail
-
1
class ContentTypeField < StructuredField
-
-
1
FIELD_NAME = 'content-type'
-
1
CAPITALIZED_FIELD = 'Content-Type'
-
-
1
def initialize(value = nil, charset = 'utf-8')
-
352
self.charset = charset
-
352
if value.class == Array
-
73
@main_type = value[0]
-
73
@sub_type = value[1]
-
73
@parameters = ParameterHash.new.merge!(value.last)
-
else
-
279
@main_type = nil
-
279
@sub_type = nil
-
279
@parameters = nil
-
279
value = strip_field(FIELD_NAME, value)
-
end
-
352
super(CAPITALIZED_FIELD, value, charset)
-
352
self.parse
-
352
self
-
end
-
-
1
def parse(val = value)
-
352
unless val.blank?
-
352
self.value = val
-
352
@element = nil
-
352
element
-
end
-
end
-
-
1
def element
-
1123
begin
-
1123
@element ||= Mail::ContentTypeElement.new(value)
-
rescue
-
attempt_to_clean
-
end
-
end
-
-
1
def attempt_to_clean
-
# Sanitize the value, handle special cases
-
@element ||= Mail::ContentTypeElement.new(sanatize(value))
-
rescue
-
# All else fails, just get the MIME media type
-
@element ||= Mail::ContentTypeElement.new(get_mime_type(value))
-
end
-
-
1
def main_type
-
1886
@main_type ||= element.main_type
-
end
-
-
1
def sub_type
-
786
@sub_type ||= element.sub_type
-
end
-
-
1
def string
-
786
"#{main_type}/#{sub_type}"
-
end
-
-
1
def default
-
465
decoded
-
end
-
-
1
alias :content_type :string
-
-
1
def parameters
-
2143
unless @parameters
-
275
@parameters = ParameterHash.new
-
287
element.parameters.each { |p| @parameters.merge!(p) }
-
end
-
2143
@parameters
-
end
-
-
1
def ContentTypeField.with_boundary(type)
-
new("#{type}; boundary=#{generate_boundary}")
-
end
-
-
1
def ContentTypeField.generate_boundary
-
34
"--==_mimepart_#{Mail.random_tag}"
-
end
-
-
1
def value
-
704
if @value.class == Array
-
73
"#{@main_type}/#{@sub_type}; #{stringify(parameters)}"
-
else
-
631
@value
-
end
-
end
-
-
1
def stringify(params)
-
175
params.map { |k,v| "#{k}=#{Encodings.param_encode(v)}" }.join("; ")
-
end
-
-
1
def filename
-
case
-
when parameters['filename']
-
44
@filename = parameters['filename']
-
when parameters['name']
-
@filename = parameters['name']
-
else
-
102
@filename = nil
-
146
end
-
146
@filename
-
end
-
-
# TODO: Fix this up
-
1
def encoded
-
69
if parameters.length > 0
-
69
p = ";\r\n\s#{parameters.encoded}"
-
else
-
p = ""
-
end
-
69
"#{CAPITALIZED_FIELD}: #{content_type}#{p}\r\n"
-
end
-
-
1
def decoded
-
466
if parameters.length > 0
-
351
p = "; #{parameters.decoded}"
-
else
-
115
p = ""
-
end
-
466
"#{content_type}" + p
-
end
-
-
1
private
-
-
1
def method_missing(name, *args, &block)
-
if name.to_s =~ /(\w+)=/
-
self.parameters[$1] = args.first
-
@value = "#{content_type}; #{stringify(parameters)}"
-
else
-
super
-
end
-
end
-
-
# Various special cases from random emails found that I am not going to change
-
# the parser for
-
1
def sanatize( val )
-
-
# TODO: check if there are cases where whitespace is not a separator
-
val = val.tr(' ',';').
-
squeeze(';').
-
gsub(';', '; '). #use '; ' as a separator (or EOL)
-
gsub(/;\s*$/,'') #remove trailing to keep examples below
-
-
if val =~ /(boundary=(\S*))/i
-
val = "#{$`.downcase}boundary=#{$2}#{$'.downcase}"
-
else
-
val.downcase!
-
end
-
-
case
-
when val.chomp =~ /^\s*([\w\-]+)\/([\w\-]+)\s*;;+(.*)$/i
-
# Handles 'text/plain;; format="flowed"' (double semi colon)
-
"#{$1}/#{$2}; #{$3}"
-
when val.chomp =~ /^\s*([\w\-]+)\/([\w\-]+)\s*;\s?(ISO[\w\-]+)$/i
-
# Microsoft helper:
-
# Handles 'type/subtype;ISO-8559-1'
-
"#{$1}/#{$2}; charset=#{quote_atom($3)}"
-
when val.chomp =~ /^text;?$/i
-
# Handles 'text;' and 'text'
-
"text/plain;"
-
when val.chomp =~ /^(\w+);\s(.*)$/i
-
# Handles 'text; <parameters>'
-
"text/plain; #{$2}"
-
when val =~ /([\w\-]+\/[\w\-]+);\scharset="charset="(\w+)""/i
-
# Handles text/html; charset="charset="GB2312""
-
"#{$1}; charset=#{quote_atom($2)}"
-
when val =~ /([\w\-]+\/[\w\-]+);\s+(.*)/i
-
type = $1
-
# Handles misquoted param values
-
# e.g: application/octet-stream; name=archiveshelp1[1].htm
-
# and: audio/x-midi;\r\n\sname=Part .exe
-
params = $2.to_s.split(/\s+/)
-
params = params.map { |i| i.to_s.chomp.strip }
-
params = params.map { |i| i.split(/\s*\=\s*/) }
-
params = params.map { |i| "#{i[0]}=#{dquote(i[1].to_s)}" }.join('; ')
-
"#{type}; #{params}"
-
when val =~ /^\s*$/
-
'text/plain'
-
else
-
''
-
end
-
end
-
-
1
def get_mime_type( val )
-
case
-
when val =~ /^([\w\-]+)\/([\w\-]+);.+$/i
-
"#{$1}/#{$2}"
-
else
-
'text/plain'
-
end
-
end
-
end
-
end
-
# encoding: utf-8
-
#
-
# = Date Field
-
#
-
# The Date field inherits from StructuredField and handles the Date: header
-
# field in the email.
-
#
-
# Sending date to a mail message will instantiate a Mail::Field object that
-
# has a DateField as it's field type. This includes all Mail::CommonAddress
-
# module instance methods.
-
#
-
# There must be excatly one Date field in an RFC2822 email.
-
#
-
# == Examples:
-
#
-
# mail = Mail.new
-
# mail.date = 'Mon, 24 Nov 1997 14:22:01 -0800'
-
# mail.date #=> #<DateTime: 211747170121/86400,-1/3,2299161>
-
# mail.date.to_s #=> 'Mon, 24 Nov 1997 14:22:01 -0800'
-
# mail[:date] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::DateField:0x180e1c4
-
# mail['date'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::DateField:0x180e1c4
-
# mail['Date'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::DateField:0x180e1c4
-
#
-
1
require 'mail/fields/common/common_date'
-
-
1
module Mail
-
1
class DateField < StructuredField
-
-
1
include Mail::CommonDate
-
-
1
FIELD_NAME = 'date'
-
1
CAPITALIZED_FIELD = "Date"
-
-
1
def initialize(value = nil, charset = 'utf-8')
-
54
self.charset = charset
-
54
if value.blank?
-
48
value = ::DateTime.now.strftime('%a, %d %b %Y %H:%M:%S %z')
-
else
-
6
value = strip_field(FIELD_NAME, value)
-
6
value.to_s.gsub!(/\(.*?\)/, '')
-
6
value = ::DateTime.parse(value.to_s.squeeze(" ")).strftime('%a, %d %b %Y %H:%M:%S %z')
-
end
-
54
super(CAPITALIZED_FIELD, value, charset)
-
rescue ArgumentError => e
-
raise e unless "invalid date"==e.message
-
end
-
-
1
def encoded
-
69
do_encode(CAPITALIZED_FIELD)
-
end
-
-
1
def decoded
-
do_decode
-
end
-
-
end
-
end
-
# encoding: utf-8
-
#
-
# = From Field
-
#
-
# The From field inherits from StructuredField and handles the From: header
-
# field in the email.
-
#
-
# Sending from to a mail message will instantiate a Mail::Field object that
-
# has a FromField as it's field type. This includes all Mail::CommonAddress
-
# module instance metods.
-
#
-
# Only one From field can appear in a header, though it can have multiple
-
# addresses and groups of addresses.
-
#
-
# == Examples:
-
#
-
# mail = Mail.new
-
# mail.from = 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
-
# mail.from #=> ['Mikel Lindsaar <mikel@test.lindsaar.net>', 'ada@test.lindsaar.net']
-
# mail[:from] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::FromField:0x180e1c4
-
# mail['from'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::FromField:0x180e1c4
-
# mail['From'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::FromField:0x180e1c4
-
#
-
# mail[:from].encoded #=> 'from: Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net\r\n'
-
# mail[:from].decoded #=> 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
-
# mail[:from].addresses #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
# mail[:from].formatted #=> ['Mikel Lindsaar <mikel@test.lindsaar.net>', 'ada@test.lindsaar.net']
-
#
-
1
require 'mail/fields/common/common_address'
-
-
1
module Mail
-
1
class FromField < StructuredField
-
-
1
include Mail::CommonAddress
-
-
1
FIELD_NAME = 'from'
-
1
CAPITALIZED_FIELD = 'From'
-
-
1
def initialize(value = nil, charset = 'utf-8')
-
110
self.charset = charset
-
110
super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
-
110
self.parse
-
110
self
-
end
-
-
1
def encoded
-
59
do_encode(CAPITALIZED_FIELD)
-
end
-
-
1
def decoded
-
do_decode
-
end
-
-
end
-
end
-
# encoding: utf-8
-
#
-
# = In-Reply-To Field
-
#
-
# The In-Reply-To field inherits from StructuredField and handles the
-
# In-Reply-To: header field in the email.
-
#
-
# Sending in_reply_to to a mail message will instantiate a Mail::Field object that
-
# has a InReplyToField as it's field type. This includes all Mail::CommonMessageId
-
# module instance metods.
-
#
-
# Note that, the #message_ids method will return an array of message IDs without the
-
# enclosing angle brackets which per RFC are not syntactically part of the message id.
-
#
-
# Only one InReplyTo field can appear in a header, though it can have multiple
-
# Message IDs.
-
#
-
# == Examples:
-
#
-
# mail = Mail.new
-
# mail.in_reply_to = '<F6E2D0B4-CC35-4A91-BA4C-C7C712B10C13@test.me.dom>'
-
# mail.in_reply_to #=> '<F6E2D0B4-CC35-4A91-BA4C-C7C712B10C13@test.me.dom>'
-
# mail[:in_reply_to] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::InReplyToField:0x180e1c4
-
# mail['in_reply_to'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::InReplyToField:0x180e1c4
-
# mail['In-Reply-To'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::InReplyToField:0x180e1c4
-
#
-
# mail[:in_reply_to].message_ids #=> ['F6E2D0B4-CC35-4A91-BA4C-C7C712B10C13@test.me.dom']
-
#
-
1
require 'mail/fields/common/common_message_id'
-
-
1
module Mail
-
1
class InReplyToField < StructuredField
-
-
1
include Mail::CommonMessageId
-
-
1
FIELD_NAME = 'in-reply-to'
-
1
CAPITALIZED_FIELD = 'In-Reply-To'
-
-
1
def initialize(value = nil, charset = 'utf-8')
-
2
self.charset = charset
-
2
super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
-
2
self.parse
-
self
-
end
-
-
1
def encoded
-
do_encode(CAPITALIZED_FIELD)
-
end
-
-
1
def decoded
-
do_decode
-
end
-
-
end
-
end
-
# encoding: utf-8
-
#
-
# = Message-ID Field
-
#
-
# The Message-ID field inherits from StructuredField and handles the
-
# Message-ID: header field in the email.
-
#
-
# Sending message_id to a mail message will instantiate a Mail::Field object that
-
# has a MessageIdField as it's field type. This includes all Mail::CommonMessageId
-
# module instance metods.
-
#
-
# Only one MessageId field can appear in a header, and syntactically it can only have
-
# one Message ID. The message_ids method call has been left in however as it will only
-
# return the one message id, ie, an array of length 1.
-
#
-
# Note that, the #message_ids method will return an array of message IDs without the
-
# enclosing angle brackets which per RFC are not syntactically part of the message id.
-
#
-
# == Examples:
-
#
-
# mail = Mail.new
-
# mail.message_id = '<F6E2D0B4-CC35-4A91-BA4C-C7C712B10C13@test.me.dom>'
-
# mail.message_id #=> '<F6E2D0B4-CC35-4A91-BA4C-C7C712B10C13@test.me.dom>'
-
# mail[:message_id] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::MessageIdField:0x180e1c4
-
# mail['message_id'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::MessageIdField:0x180e1c4
-
# mail['Message-ID'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::MessageIdField:0x180e1c4
-
#
-
# mail[:message_id].message_id #=> 'F6E2D0B4-CC35-4A91-BA4C-C7C712B10C13@test.me.dom'
-
# mail[:message_id].message_ids #=> ['F6E2D0B4-CC35-4A91-BA4C-C7C712B10C13@test.me.dom']
-
#
-
1
require 'mail/fields/common/common_message_id'
-
-
1
module Mail
-
1
class MessageIdField < StructuredField
-
-
1
include Mail::CommonMessageId
-
-
1
FIELD_NAME = 'message-id'
-
1
CAPITALIZED_FIELD = 'Message-ID'
-
-
1
def initialize(value = nil, charset = 'utf-8')
-
45
self.charset = charset
-
45
@uniq = 1
-
45
if value.blank?
-
35
self.name = CAPITALIZED_FIELD
-
35
self.value = generate_message_id
-
else
-
10
super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
-
end
-
45
self.parse
-
45
self
-
-
end
-
-
1
def name
-
1242
'Message-ID'
-
end
-
-
1
def message_ids
-
160
[message_id]
-
end
-
-
1
def to_s
-
"<#{message_id}>"
-
end
-
-
1
def encoded
-
59
do_encode(CAPITALIZED_FIELD)
-
end
-
-
1
def decoded
-
do_decode
-
end
-
-
1
private
-
-
1
def generate_message_id
-
35
fqdn = ::Socket.gethostname
-
35
"<#{Mail.random_tag}@#{fqdn}.mail>"
-
end
-
-
end
-
end
-
# encoding: utf-8
-
#
-
#
-
#
-
1
module Mail
-
1
class MimeVersionField < StructuredField
-
-
1
FIELD_NAME = 'mime-version'
-
1
CAPITALIZED_FIELD = 'Mime-Version'
-
-
1
def initialize(value = nil, charset = 'utf-8')
-
175
self.charset = charset
-
175
if value.blank?
-
12
value = '1.0'
-
end
-
175
super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
-
175
self.parse
-
175
self
-
-
end
-
-
1
def parse(val = value)
-
175
unless val.blank?
-
175
@element = Mail::MimeVersionElement.new(val)
-
end
-
end
-
-
1
def element
-
146
@element ||= Mail::MimeVersionElement.new(value)
-
end
-
-
1
def version
-
73
"#{element.major}.#{element.minor}"
-
end
-
-
1
def major
-
element.major.to_i
-
end
-
-
1
def minor
-
element.minor.to_i
-
end
-
-
1
def encoded
-
69
"#{CAPITALIZED_FIELD}: #{version}\r\n"
-
end
-
-
1
def decoded
-
4
version
-
end
-
-
end
-
end
-
# encoding: utf-8
-
#
-
# The field names of any optional-field MUST NOT be identical to any
-
# field name specified elsewhere in this standard.
-
#
-
# optional-field = field-name ":" unstructured CRLF
-
1
require 'mail/fields/unstructured_field'
-
-
1
module Mail
-
1
class OptionalField < UnstructuredField
-
-
end
-
end
-
# encoding: utf-8
-
#
-
# = Reply-To Field
-
#
-
# The Reply-To field inherits reply-to StructuredField and handles the Reply-To: header
-
# field in the email.
-
#
-
# Sending reply_to to a mail message will instantiate a Mail::Field object that
-
# has a ReplyToField as it's field type. This includes all Mail::CommonAddress
-
# module instance metods.
-
#
-
# Only one Reply-To field can appear in a header, though it can have multiple
-
# addresses and groups of addresses.
-
#
-
# == Examples:
-
#
-
# mail = Mail.new
-
# mail.reply_to = 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
-
# mail.reply_to #=> ['Mikel Lindsaar <mikel@test.lindsaar.net>', 'ada@test.lindsaar.net']
-
# mail[:reply_to] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ReplyToField:0x180e1c4
-
# mail['reply-to'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ReplyToField:0x180e1c4
-
# mail['Reply-To'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ReplyToField:0x180e1c4
-
#
-
# mail[:reply_to].encoded #=> 'Reply-To: Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net\r\n'
-
# mail[:reply_to].decoded #=> 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
-
# mail[:reply_to].addresses #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
# mail[:reply_to].formatted #=> ['Mikel Lindsaar <mikel@test.lindsaar.net>', 'ada@test.lindsaar.net']
-
#
-
1
require 'mail/fields/common/common_address'
-
-
1
module Mail
-
1
class ReplyToField < StructuredField
-
-
1
include Mail::CommonAddress
-
-
1
FIELD_NAME = 'reply-to'
-
1
CAPITALIZED_FIELD = 'Reply-To'
-
-
1
def initialize(value = nil, charset = 'utf-8')
-
64
self.charset = charset
-
64
super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
-
64
self.parse
-
64
self
-
end
-
-
1
def encoded
-
31
do_encode(CAPITALIZED_FIELD)
-
end
-
-
1
def decoded
-
do_decode
-
end
-
-
end
-
end
-
# encoding: utf-8
-
1
require 'mail/fields/common/common_field'
-
-
1
module Mail
-
# Provides access to a structured header field
-
#
-
# ===Per RFC 2822:
-
# 2.2.2. Structured Header Field Bodies
-
#
-
# Some field bodies in this standard have specific syntactical
-
# structure more restrictive than the unstructured field bodies
-
# described above. These are referred to as "structured" field bodies.
-
# Structured field bodies are sequences of specific lexical tokens as
-
# described in sections 3 and 4 of this standard. Many of these tokens
-
# are allowed (according to their syntax) to be introduced or end with
-
# comments (as described in section 3.2.3) as well as the space (SP,
-
# ASCII value 32) and horizontal tab (HTAB, ASCII value 9) characters
-
# (together known as the white space characters, WSP), and those WSP
-
# characters are subject to header "folding" and "unfolding" as
-
# described in section 2.2.3. Semantic analysis of structured field
-
# bodies is given along with their syntax.
-
1
class StructuredField
-
-
1
include Mail::CommonField
-
1
include Mail::Utilities
-
-
1
def initialize(name = nil, value = nil, charset = nil)
-
1017
self.name = name
-
1017
self.value = value
-
1017
self.charset = charset
-
1017
self
-
end
-
-
1
def charset
-
160
@charset
-
end
-
-
1
def charset=(val)
-
2067
@charset = val
-
end
-
-
1
def default
-
123
decoded
-
end
-
-
1
def errors
-
123
[]
-
end
-
-
end
-
end
-
# encoding: utf-8
-
#
-
# subject = "Subject:" unstructured CRLF
-
1
module Mail
-
1
class SubjectField < UnstructuredField
-
-
1
FIELD_NAME = 'subject'
-
1
CAPITALIZED_FIELD = "Subject"
-
-
1
def initialize(value = nil, charset = 'utf-8')
-
120
self.charset = charset
-
120
super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
-
end
-
-
end
-
end
-
# encoding: utf-8
-
#
-
# = To Field
-
#
-
# The To field inherits to StructuredField and handles the To: header
-
# field in the email.
-
#
-
# Sending to to a mail message will instantiate a Mail::Field object that
-
# has a ToField as it's field type. This includes all Mail::CommonAddress
-
# module instance metods.
-
#
-
# Only one To field can appear in a header, though it can have multiple
-
# addresses and groups of addresses.
-
#
-
# == Examples:
-
#
-
# mail = Mail.new
-
# mail.to = 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
-
# mail.to #=> ['Mikel Lindsaar <mikel@test.lindsaar.net>', 'ada@test.lindsaar.net']
-
# mail[:to] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ToField:0x180e1c4
-
# mail['to'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ToField:0x180e1c4
-
# mail['To'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ToField:0x180e1c4
-
#
-
# mail[:to].encoded #=> 'To: Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net\r\n'
-
# mail[:to].decoded #=> 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
-
# mail[:to].addresses #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
# mail[:to].formatted #=> ['Mikel Lindsaar <mikel@test.lindsaar.net>', 'ada@test.lindsaar.net']
-
#
-
1
require 'mail/fields/common/common_address'
-
-
1
module Mail
-
1
class ToField < StructuredField
-
-
1
include Mail::CommonAddress
-
-
1
FIELD_NAME = 'to'
-
1
CAPITALIZED_FIELD = 'To'
-
-
1
def initialize(value = nil, charset = 'utf-8')
-
113
self.charset = charset
-
113
super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
-
113
self.parse
-
113
self
-
end
-
-
1
def encoded
-
59
do_encode(CAPITALIZED_FIELD)
-
end
-
-
1
def decoded
-
do_decode
-
end
-
-
end
-
end
-
# encoding: utf-8
-
1
require 'mail/fields/common/common_field'
-
-
1
module Mail
-
# Provides access to an unstructured header field
-
#
-
# ===Per RFC 2822:
-
# 2.2.1. Unstructured Header Field Bodies
-
#
-
# Some field bodies in this standard are defined simply as
-
# "unstructured" (which is specified below as any US-ASCII characters,
-
# except for CR and LF) with no further restrictions. These are
-
# referred to as unstructured field bodies. Semantically, unstructured
-
# field bodies are simply to be treated as a single line of characters
-
# with no further processing (except for header "folding" and
-
# "unfolding" as described in section 2.2.3).
-
1
class UnstructuredField
-
-
1
include Mail::CommonField
-
1
include Mail::Utilities
-
-
1
attr_accessor :charset
-
1
attr_reader :errors
-
-
1
def initialize(name, value, charset = nil)
-
159
@errors = []
-
159
if charset
-
157
self.charset = charset
-
else
-
2
if value.to_s.respond_to?(:encoding)
-
2
self.charset = value.to_s.encoding
-
else
-
self.charset = $KCODE
-
end
-
end
-
159
self.name = name
-
159
self.value = value
-
159
self
-
end
-
-
1
def encoded
-
84
do_encode
-
end
-
-
1
def decoded
-
140
do_decode
-
end
-
-
1
def default
-
46
decoded
-
end
-
-
1
def parse # An unstructured field does not parse
-
self
-
end
-
-
1
private
-
-
1
def do_encode
-
84
value.nil? ? '' : "#{wrapped_value}\r\n"
-
end
-
-
1
def do_decode
-
140
result = value.blank? ? nil : Encodings.decode_encode(value, :decode)
-
140
result.encode!(value.encoding || "UTF-8") if RUBY_VERSION >= '1.9' && !result.blank?
-
140
result
-
end
-
-
# 2.2.3. Long Header Fields
-
#
-
# Each header field is logically a single line of characters comprising
-
# the field name, the colon, and the field body. For convenience
-
# however, and to deal with the 998/78 character limitations per line,
-
# the field body portion of a header field can be split into a multiple
-
# line representation; this is called "folding". The general rule is
-
# that wherever this standard allows for folding white space (not
-
# simply WSP characters), a CRLF may be inserted before any WSP. For
-
# example, the header field:
-
#
-
# Subject: This is a test
-
#
-
# can be represented as:
-
#
-
# Subject: This
-
# is a test
-
#
-
# Note: Though structured field bodies are defined in such a way that
-
# folding can take place between many of the lexical tokens (and even
-
# within some of the lexical tokens), folding SHOULD be limited to
-
# placing the CRLF at higher-level syntactic breaks. For instance, if
-
# a field body is defined as comma-separated values, it is recommended
-
# that folding occur after the comma separating the structured items in
-
# preference to other places where the field could be folded, even if
-
# it is allowed elsewhere.
-
1
def wrapped_value # :nodoc:
-
84
wrap_lines(name, fold("#{name}: ".length))
-
end
-
-
# 6.2. Display of 'encoded-word's
-
#
-
# When displaying a particular header field that contains multiple
-
# 'encoded-word's, any 'linear-white-space' that separates a pair of
-
# adjacent 'encoded-word's is ignored. (This is to allow the use of
-
# multiple 'encoded-word's to represent long strings of unencoded text,
-
# without having to separate 'encoded-word's where spaces occur in the
-
# unencoded text.)
-
1
def wrap_lines(name, folded_lines)
-
84
result = ["#{name}: #{folded_lines.shift}"]
-
84
result.concat(folded_lines)
-
84
result.join("\r\n\s")
-
end
-
-
1
def fold(prepend = 0) # :nodoc:
-
84
encoding = normalized_encoding
-
84
decoded_string = decoded.to_s
-
84
should_encode = decoded_string.not_ascii_only?
-
84
if should_encode
-
1
first = true
-
1
words = decoded_string.split(/[ \t]/).map do |word|
-
4
if first
-
1
first = !first
-
else
-
3
word = " " << word
-
end
-
4
if word.not_ascii_only?
-
3
word
-
else
-
1
word.scan(/.{7}|.+$/)
-
end
-
end.flatten
-
else
-
83
words = decoded_string.split(/[ \t]/)
-
end
-
-
84
folded_lines = []
-
84
while !words.empty?
-
84
limit = 78 - prepend
-
84
limit = limit - 7 - encoding.length if should_encode
-
84
line = ""
-
84
while !words.empty?
-
267
break unless word = words.first.dup
-
267
word.encode!(charset) if defined?(Encoding) && charset
-
267
word = encode(word) if should_encode
-
267
word = encode_crlf(word)
-
# Skip to next line if we're going to go past the limit
-
# Unless this is the first word, in which case we're going to add it anyway
-
# Note: This means that a word that's longer than 998 characters is going to break the spec. Please fix if this is a problem for you.
-
# (The fix, it seems, would be to use encoded-word encoding on it, because that way you can break it across multiple lines and
-
# the linebreak will be ignored)
-
267
break if !line.empty? && (line.length + word.length + 1 > limit)
-
# Remove the word from the queue ...
-
267
words.shift
-
# Add word separator
-
267
line << " " unless (line.empty? || should_encode)
-
# ... add it in encoded form to the current line
-
267
line << word
-
end
-
# Encode the line if necessary
-
84
line = "=?#{encoding}?Q?#{line}?=" if should_encode
-
# Add the line to the output and reset the prepend
-
84
folded_lines << line
-
84
prepend = 0
-
end
-
84
folded_lines
-
end
-
-
1
def encode(value)
-
4
value = [value].pack("M").gsub("=\n", '')
-
4
value.gsub!(/"/, '=22')
-
4
value.gsub!(/\(/, '=28')
-
4
value.gsub!(/\)/, '=29')
-
4
value.gsub!(/\?/, '=3F')
-
4
value.gsub!(/_/, '=5F')
-
4
value.gsub!(/ /, '_')
-
4
value
-
end
-
-
1
def encode_crlf(value)
-
267
value.gsub!("\r", '=0D')
-
267
value.gsub!("\n", '=0A')
-
267
value
-
end
-
-
1
def normalized_encoding
-
84
encoding = charset.to_s.upcase.gsub('_', '-')
-
84
encoding = 'UTF-8' if encoding == 'UTF8' # Ruby 1.8.x and $KCODE == 'u'
-
84
encoding
-
end
-
-
end
-
end
-
# encoding: utf-8
-
1
module Mail
-
-
# Provides access to a header object.
-
#
-
# ===Per RFC2822
-
#
-
# 2.2. Header Fields
-
#
-
# Header fields are lines composed of a field name, followed by a colon
-
# (":"), followed by a field body, and terminated by CRLF. A field
-
# name MUST be composed of printable US-ASCII characters (i.e.,
-
# characters that have values between 33 and 126, inclusive), except
-
# colon. A field body may be composed of any US-ASCII characters,
-
# except for CR and LF. However, a field body may contain CRLF when
-
# used in header "folding" and "unfolding" as described in section
-
# 2.2.3. All field bodies MUST conform to the syntax described in
-
# sections 3 and 4 of this standard.
-
1
class Header
-
1
include Patterns
-
1
include Utilities
-
1
include Enumerable
-
-
# Creates a new header object.
-
#
-
# Accepts raw text or nothing. If given raw text will attempt to parse
-
# it and split it into the various fields, instantiating each field as
-
# it goes.
-
#
-
# If it finds a field that should be a structured field (such as content
-
# type), but it fails to parse it, it will simply make it an unstructured
-
# field and leave it alone. This will mean that the data is preserved but
-
# no automatic processing of that field will happen. If you find one of
-
# these cases, please make a patch and send it in, or at the least, send
-
# me the example so we can fix it.
-
1
def initialize(header_text = nil, charset = nil)
-
235
@errors = []
-
235
@charset = charset
-
235
self.raw_source = header_text.to_crlf
-
235
split_header if header_text
-
end
-
-
# The preserved raw source of the header as you passed it in, untouched
-
# for your Regexing glory.
-
1
def raw_source
-
5
@raw_source
-
end
-
-
# Returns an array of all the fields in the header in order that they
-
# were read in.
-
1
def fields
-
8903
@fields ||= FieldList.new
-
end
-
-
# 3.6. Field definitions
-
#
-
# It is important to note that the header fields are not guaranteed to
-
# be in a particular order. They may appear in any order, and they
-
# have been known to be reordered occasionally when transported over
-
# the Internet. However, for the purposes of this standard, header
-
# fields SHOULD NOT be reordered when a message is transported or
-
# transformed. More importantly, the trace header fields and resent
-
# header fields MUST NOT be reordered, and SHOULD be kept in blocks
-
# prepended to the message. See sections 3.6.6 and 3.6.7 for more
-
# information.
-
#
-
# Populates the fields container with Field objects in the order it
-
# receives them in.
-
#
-
# Acceps an array of field string values, for example:
-
#
-
# h = Header.new
-
# h.fields = ['From: mikel@me.com', 'To: bob@you.com']
-
1
def fields=(unfolded_fields)
-
5
@fields = Mail::FieldList.new
-
5
warn "Warning: more than 1000 header fields only using the first 1000" if unfolded_fields.length > 1000
-
5
unfolded_fields[0..1000].each do |field|
-
-
12
field = Field.new(field, nil, charset)
-
12
field.errors.each { |error| self.errors << error }
-
12
selected = select_field_for(field.name)
-
-
12
if selected.any? && limited_field?(field.name)
-
selected.first.update(field.name, field.value)
-
else
-
12
@fields << field
-
end
-
end
-
-
end
-
-
1
def errors
-
@errors
-
end
-
-
# 3.6. Field definitions
-
#
-
# The following table indicates limits on the number of times each
-
# field may occur in a message header as well as any special
-
# limitations on the use of those fields. An asterisk next to a value
-
# in the minimum or maximum column indicates that a special restriction
-
# appears in the Notes column.
-
#
-
# <snip table from 3.6>
-
#
-
# As per RFC, many fields can appear more than once, we will return a string
-
# of the value if there is only one header, or if there is more than one
-
# matching header, will return an array of values in order that they appear
-
# in the header ordered from top to bottom.
-
#
-
# Example:
-
#
-
# h = Header.new
-
# h.fields = ['To: mikel@me.com', 'X-Mail-SPAM: 15', 'X-Mail-SPAM: 20']
-
# h['To'] #=> 'mikel@me.com'
-
# h['X-Mail-SPAM'] #=> ['15', '20']
-
1
def [](name)
-
6313
name = dasherize(name).downcase
-
6313
selected = select_field_for(name)
-
case
-
when selected.length > 1
-
selected.map { |f| f }
-
when !selected.blank?
-
4138
selected.first
-
else
-
nil
-
6313
end
-
end
-
-
# Sets the FIRST matching field in the header to passed value, or deletes
-
# the FIRST field matched from the header if passed nil
-
#
-
# Example:
-
#
-
# h = Header.new
-
# h.fields = ['To: mikel@me.com', 'X-Mail-SPAM: 15', 'X-Mail-SPAM: 20']
-
# h['To'] = 'bob@you.com'
-
# h['To'] #=> 'bob@you.com'
-
# h['X-Mail-SPAM'] = '10000'
-
# h['X-Mail-SPAM'] # => ['15', '20', '10000']
-
# h['X-Mail-SPAM'] = nil
-
# h['X-Mail-SPAM'] # => nil
-
1
def []=(name, value)
-
1196
name = dasherize(name)
-
1196
fn = name.downcase
-
1196
selected = select_field_for(fn)
-
-
case
-
# User wants to delete the field
-
when !selected.blank? && value == nil
-
fields.delete_if { |f| selected.include?(f) }
-
-
# User wants to change the field
-
when !selected.blank? && limited_field?(fn)
-
160
selected.first.update(fn, value)
-
-
# User wants to create the field
-
else
-
# Need to insert in correct order for trace fields
-
1036
self.fields << Field.new(name.to_s, value, charset)
-
1196
end
-
end
-
-
1
def charset
-
1048
params = self[:content_type].parameters rescue nil
-
1048
if params
-
249
params[:charset]
-
else
-
799
@charset
-
end
-
end
-
-
1
def charset=(val)
-
406
params = self[:content_type].parameters rescue nil
-
406
if params
-
169
params[:charset] = val
-
end
-
406
@charset = val
-
end
-
-
1
LIMITED_FIELDS = %w[ date from sender reply-to to cc bcc
-
message-id in-reply-to references subject
-
return-path content-type mime-version
-
content-transfer-encoding content-description
-
content-id content-disposition content-location]
-
-
1
def encoded
-
69
buffer = ''
-
69
fields.each do |field|
-
579
buffer << field.encoded
-
end
-
69
buffer
-
end
-
-
1
def to_s
-
encoded
-
end
-
-
1
def decoded
-
raise NoMethodError, 'Can not decode an entire header as there could be character set conflicts, try calling #decoded on the various fields.'
-
end
-
-
1
def field_summary
-
fields.map { |f| "<#{f.name}: #{f.value}>" }.join(", ")
-
end
-
-
# Returns true if the header has a Message-ID defined (empty or not)
-
1
def has_message_id?
-
648
!fields.select { |f| f.responsible_for?('Message-ID') }.empty?
-
end
-
-
# Returns true if the header has a Content-ID defined (empty or not)
-
1
def has_content_id?
-
116
!fields.select { |f| f.responsible_for?('Content-ID') }.empty?
-
end
-
-
# Returns true if the header has a Date defined (empty or not)
-
1
def has_date?
-
683
!fields.select { |f| f.responsible_for?('Date') }.empty?
-
end
-
-
# Returns true if the header has a MIME version defined (empty or not)
-
1
def has_mime_version?
-
731
!fields.select { |f| f.responsible_for?('Mime-Version') }.empty?
-
end
-
-
1
private
-
-
1
def raw_source=(val)
-
235
@raw_source = val
-
end
-
-
# 2.2.3. Long Header Fields
-
#
-
# The process of moving from this folded multiple-line representation
-
# of a header field to its single line representation is called
-
# "unfolding". Unfolding is accomplished by simply removing any CRLF
-
# that is immediately followed by WSP. Each header field should be
-
# treated in its unfolded form for further syntactic and semantic
-
# evaluation.
-
1
def unfold(string)
-
5
string.gsub(/#{CRLF}#{WSP}+/, ' ').gsub(/#{WSP}+/, ' ')
-
end
-
-
# Returns the header with all the folds removed
-
1
def unfolded_header
-
5
@unfolded_header ||= unfold(raw_source)
-
end
-
-
# Splits an unfolded and line break cleaned header into individual field
-
# strings.
-
1
def split_header
-
5
self.fields = unfolded_header.split(CRLF)
-
end
-
-
1
def select_field_for(name)
-
36021
fields.select { |f| f.responsible_for?(name.to_s) }
-
end
-
-
1
def limited_field?(name)
-
160
LIMITED_FIELDS.include?(name.to_s.downcase)
-
end
-
-
end
-
end
-
# encoding: utf-8
-
-
# This is an almost cut and paste from ActiveSupport v3.0.6, copied in here so that Mail
-
# itself does not depend on ActiveSupport to avoid versioning conflicts
-
-
1
module Mail
-
1
class IndifferentHash < Hash
-
-
1
def initialize(constructor = {})
-
574
if constructor.is_a?(Hash)
-
574
super()
-
574
update(constructor)
-
else
-
super(constructor)
-
end
-
end
-
-
1
def default(key = nil)
-
246
if key.is_a?(Symbol) && include?(key = key.to_s)
-
self[key]
-
else
-
246
super
-
end
-
end
-
-
1
def self.new_from_hash_copying_default(hash)
-
IndifferentHash.new(hash).tap do |new_hash|
-
new_hash.default = hash.default
-
end
-
end
-
-
1
alias_method :regular_writer, :[]= unless method_defined?(:regular_writer)
-
1
alias_method :regular_update, :update unless method_defined?(:regular_update)
-
-
# Assigns a new value to the hash:
-
#
-
# hash = HashWithIndifferentAccess.new
-
# hash[:key] = "value"
-
#
-
1
def []=(key, value)
-
239
regular_writer(convert_key(key), convert_value(value))
-
end
-
-
1
alias_method :store, :[]=
-
-
# Updates the instantized hash with values from the second:
-
#
-
# hash_1 = HashWithIndifferentAccess.new
-
# hash_1[:key] = "value"
-
#
-
# hash_2 = HashWithIndifferentAccess.new
-
# hash_2[:key] = "New Value!"
-
#
-
# hash_1.update(hash_2) # => {"key"=>"New Value!"}
-
#
-
1
def update(other_hash)
-
1302
other_hash.each_pair { |key, value| regular_writer(convert_key(key), convert_value(value)) }
-
821
self
-
end
-
-
1
alias_method :merge!, :update
-
-
# Checks the hash for a key matching the argument passed in:
-
#
-
# hash = HashWithIndifferentAccess.new
-
# hash["key"] = "value"
-
# hash.key? :key # => true
-
# hash.key? "key" # => true
-
#
-
1
def key?(key)
-
40
super(convert_key(key))
-
end
-
-
1
alias_method :include?, :key?
-
1
alias_method :has_key?, :key?
-
1
alias_method :member?, :key?
-
-
# Fetches the value for the specified key, same as doing hash[key]
-
1
def fetch(key, *extras)
-
super(convert_key(key), *extras)
-
end
-
-
# Returns an array of the values at the specified indices:
-
#
-
# hash = HashWithIndifferentAccess.new
-
# hash[:a] = "x"
-
# hash[:b] = "y"
-
# hash.values_at("a", "b") # => ["x", "y"]
-
#
-
1
def values_at(*indices)
-
indices.collect {|key| self[convert_key(key)]}
-
end
-
-
# Returns an exact copy of the hash.
-
1
def dup
-
IndifferentHash.new(self)
-
end
-
-
# Merges the instantized and the specified hashes together, giving precedence to the values from the second hash
-
# Does not overwrite the existing hash.
-
1
def merge(hash)
-
self.dup.update(hash)
-
end
-
-
# Performs the opposite of merge, with the keys and values from the first hash taking precedence over the second.
-
# This overloaded definition prevents returning a regular hash, if reverse_merge is called on a HashWithDifferentAccess.
-
1
def reverse_merge(other_hash)
-
super self.class.new_from_hash_copying_default(other_hash)
-
end
-
-
1
def reverse_merge!(other_hash)
-
replace(reverse_merge( other_hash ))
-
end
-
-
# Removes a specified key from the hash.
-
1
def delete(key)
-
super(convert_key(key))
-
end
-
-
1
def stringify_keys!; self end
-
1
def stringify_keys; dup end
-
1
def symbolize_keys; to_hash.symbolize_keys end
-
1
def to_options!; self end
-
-
1
def to_hash
-
Hash.new(default).merge!(self)
-
end
-
-
1
protected
-
-
1
def convert_key(key)
-
760
key.kind_of?(Symbol) ? key.to_s : key
-
end
-
-
1
def convert_value(value)
-
720
if value.class == Hash
-
self.class.new_from_hash_copying_default(value)
-
720
elsif value.is_a?(Array)
-
value.dup.replace(value.map { |e| convert_value(e) })
-
else
-
720
value
-
end
-
end
-
-
end
-
end
-
# encoding: utf-8
-
1
module Mail
-
-
# Allows you to create a new Mail::Message object.
-
#
-
# You can make an email via passing a string or passing a block.
-
#
-
# For example, the following two examples will create the same email
-
# message:
-
#
-
# Creating via a string:
-
#
-
# string = "To: mikel@test.lindsaar.net\r\n"
-
# string << "From: bob@test.lindsaar.net\r\n"
-
# string << "Subject: This is an email\r\n"
-
# string << "\r\n"
-
# string << "This is the body"
-
# Mail.new(string)
-
#
-
# Or creating via a block:
-
#
-
# message = Mail.new do
-
# to 'mikel@test.lindsaar.net'
-
# from 'bob@test.lindsaar.net'
-
# subject 'This is an email'
-
# body 'This is the body'
-
# end
-
#
-
# Or creating via a hash (or hash like object):
-
#
-
# message = Mail.new({:to => 'mikel@test.lindsaar.net',
-
# 'from' => 'bob@test.lindsaar.net',
-
# :subject => 'This is an email',
-
# :body => 'This is the body' })
-
#
-
# Note, the hash keys can be strings or symbols, the passed in object
-
# does not need to be a hash, it just needs to respond to :each_pair
-
# and yield each key value pair.
-
#
-
# As a side note, you can also create a new email through creating
-
# a Mail::Message object directly and then passing in values via string,
-
# symbol or direct method calls. See Mail::Message for more information.
-
#
-
# mail = Mail.new
-
# mail.to = 'mikel@test.lindsaar.net'
-
# mail[:from] = 'bob@test.lindsaar.net'
-
# mail['subject'] = 'This is an email'
-
# mail.body = 'This is the body'
-
1
def self.new(*args, &block)
-
166
Message.new(args, &block)
-
end
-
-
# Sets the default delivery method and retriever method for all new Mail objects.
-
# The delivery_method and retriever_method default to :smtp and :pop3, with defaults
-
# set.
-
#
-
# So sending a new email, if you have an SMTP server running on localhost is
-
# as easy as:
-
#
-
# Mail.deliver do
-
# to 'mikel@test.lindsaar.net'
-
# from 'bob@test.lindsaar.net'
-
# subject 'hi there!'
-
# body 'this is a body'
-
# end
-
#
-
# If you do not specify anything, you will get the following equivalent code set in
-
# every new mail object:
-
#
-
# Mail.defaults do
-
# delivery_method :smtp, { :address => "localhost",
-
# :port => 25,
-
# :domain => 'localhost.localdomain',
-
# :user_name => nil,
-
# :password => nil,
-
# :authentication => nil,
-
# :enable_starttls_auto => true }
-
#
-
# retriever_method :pop3, { :address => "localhost",
-
# :port => 995,
-
# :user_name => nil,
-
# :password => nil,
-
# :enable_ssl => true }
-
# end
-
#
-
# Mail.delivery_method.new #=> Mail::SMTP instance
-
# Mail.retriever_method.new #=> Mail::POP3 instance
-
#
-
# Each mail object inherits the default set in Mail.delivery_method, however, on
-
# a per email basis, you can override the method:
-
#
-
# mail.delivery_method :sendmail
-
#
-
# Or you can override the method and pass in settings:
-
#
-
# mail.delivery_method :sendmail, { :address => 'some.host' }
-
#
-
# You can also just modify the settings:
-
#
-
# mail.delivery_settings = { :address => 'some.host' }
-
#
-
# The passed in hash is just merged against the defaults with +merge!+ and the result
-
# assigned the mail object. So the above example will change only the :address value
-
# of the global smtp_settings to be 'some.host', keeping all other values
-
1
def self.defaults(&block)
-
Configuration.instance.instance_eval(&block)
-
end
-
-
# Returns the delivery method selected, defaults to an instance of Mail::SMTP
-
1
def self.delivery_method
-
235
Configuration.instance.delivery_method
-
end
-
-
# Returns the retriever method selected, defaults to an instance of Mail::POP3
-
1
def self.retriever_method
-
Configuration.instance.retriever_method
-
end
-
-
# Send an email using the default configuration. You do need to set a default
-
# configuration first before you use self.deliver, if you don't, an appropriate
-
# error will be raised telling you to.
-
#
-
# If you do not specify a delivery type, SMTP will be used.
-
#
-
# Mail.deliver do
-
# to 'mikel@test.lindsaar.net'
-
# from 'ada@test.lindsaar.net'
-
# subject 'This is a test email'
-
# body 'Not much to say here'
-
# end
-
#
-
# You can also do:
-
#
-
# mail = Mail.read('email.eml')
-
# mail.deliver!
-
#
-
# And your email object will be created and sent.
-
1
def self.deliver(*args, &block)
-
mail = self.new(args, &block)
-
mail.deliver
-
mail
-
end
-
-
# Find emails from the default retriever
-
# See Mail::Retriever for a complete documentation.
-
1
def self.find(*args, &block)
-
retriever_method.find(*args, &block)
-
end
-
-
# Finds and then deletes retrieved emails from the default retriever
-
# See Mail::Retriever for a complete documentation.
-
1
def self.find_and_delete(*args, &block)
-
retriever_method.find_and_delete(*args, &block)
-
end
-
-
# Receive the first email(s) from the default retriever
-
# See Mail::Retriever for a complete documentation.
-
1
def self.first(*args, &block)
-
retriever_method.first(*args, &block)
-
end
-
-
# Receive the first email(s) from the default retriever
-
# See Mail::Retriever for a complete documentation.
-
1
def self.last(*args, &block)
-
retriever_method.last(*args, &block)
-
end
-
-
# Receive all emails from the default retriever
-
# See Mail::Retriever for a complete documentation.
-
1
def self.all(*args, &block)
-
retriever_method.all(*args, &block)
-
end
-
-
# Reads in an email message from a path and instantiates it as a new Mail::Message
-
1
def self.read(filename)
-
self.new(File.open(filename, 'rb') { |f| f.read })
-
end
-
-
# Delete all emails from the default retriever
-
# See Mail::Retriever for a complete documentation.
-
1
def self.delete_all(*args, &block)
-
retriever_method.delete_all(*args, &block)
-
end
-
-
# Instantiates a new Mail::Message using a string
-
1
def Mail.read_from_string(mail_as_string)
-
Mail.new(mail_as_string)
-
end
-
-
1
def Mail.connection(&block)
-
retriever_method.connection(&block)
-
end
-
-
# Initialize the observers and interceptors arrays
-
1
@@delivery_notification_observers = []
-
1
@@delivery_interceptors = []
-
-
# You can register an object to be informed of every email that is sent through
-
# this method.
-
#
-
# Your object needs to respond to a single method #delivered_email(mail)
-
# which receives the email that is sent.
-
1
def self.register_observer(observer)
-
4
unless @@delivery_notification_observers.include?(observer)
-
2
@@delivery_notification_observers << observer
-
end
-
end
-
-
# You can register an object to be given every mail object that will be sent,
-
# before it is sent. So if you want to add special headers or modify any
-
# email that gets sent through the Mail library, you can do so.
-
#
-
# Your object needs to respond to a single method #delivering_email(mail)
-
# which receives the email that is about to be sent. Make your modifications
-
# directly to this object.
-
1
def self.register_interceptor(interceptor)
-
4
unless @@delivery_interceptors.include?(interceptor)
-
2
@@delivery_interceptors << interceptor
-
end
-
end
-
-
1
def self.inform_observers(mail)
-
37
@@delivery_notification_observers.each do |observer|
-
48
observer.delivered_email(mail)
-
end
-
end
-
-
1
def self.inform_interceptors(mail)
-
39
@@delivery_interceptors.each do |interceptor|
-
46
interceptor.delivering_email(mail)
-
end
-
end
-
-
1
protected
-
-
1
def self.random_tag
-
92
t = Time.now
-
92
sprintf('%x%x_%x%x%d%x',
-
t.to_i, t.tv_usec,
-
$$, Thread.current.object_id.abs, self.uniq, rand(255))
-
end
-
-
1
private
-
-
1
def self.something_random
-
1
(Thread.current.object_id * rand(255) / Time.now.to_f).to_s.slice(-3..-1).to_i
-
end
-
-
1
def self.uniq
-
92
@@uniq += 1
-
end
-
-
1
@@uniq = self.something_random
-
-
end
-
# encoding: utf-8
-
1
require "yaml"
-
-
1
module Mail
-
# The Message class provides a single point of access to all things to do with an
-
# email message.
-
#
-
# You create a new email message by calling the Mail::Message.new method, or just
-
# Mail.new
-
#
-
# A Message object by default has the following objects inside it:
-
#
-
# * A Header object which contains all information and settings of the header of the email
-
# * Body object which contains all parts of the email that are not part of the header, this
-
# includes any attachments, body text, MIME parts etc.
-
#
-
# ==Per RFC2822
-
#
-
# 2.1. General Description
-
#
-
# At the most basic level, a message is a series of characters. A
-
# message that is conformant with this standard is comprised of
-
# characters with values in the range 1 through 127 and interpreted as
-
# US-ASCII characters [ASCII]. For brevity, this document sometimes
-
# refers to this range of characters as simply "US-ASCII characters".
-
#
-
# Note: This standard specifies that messages are made up of characters
-
# in the US-ASCII range of 1 through 127. There are other documents,
-
# specifically the MIME document series [RFC2045, RFC2046, RFC2047,
-
# RFC2048, RFC2049], that extend this standard to allow for values
-
# outside of that range. Discussion of those mechanisms is not within
-
# the scope of this standard.
-
#
-
# Messages are divided into lines of characters. A line is a series of
-
# characters that is delimited with the two characters carriage-return
-
# and line-feed; that is, the carriage return (CR) character (ASCII
-
# value 13) followed immediately by the line feed (LF) character (ASCII
-
# value 10). (The carriage-return/line-feed pair is usually written in
-
# this document as "CRLF".)
-
#
-
# A message consists of header fields (collectively called "the header
-
# of the message") followed, optionally, by a body. The header is a
-
# sequence of lines of characters with special syntax as defined in
-
# this standard. The body is simply a sequence of characters that
-
# follows the header and is separated from the header by an empty line
-
# (i.e., a line with nothing preceding the CRLF).
-
1
class Message
-
-
1
include Patterns
-
1
include Utilities
-
-
# ==Making an email
-
#
-
# You can make an new mail object via a block, passing a string, file or direct assignment.
-
#
-
# ===Making an email via a block
-
#
-
# mail = Mail.new do
-
# from 'mikel@test.lindsaar.net'
-
# to 'you@test.lindsaar.net'
-
# subject 'This is a test email'
-
# body File.read('body.txt')
-
# end
-
#
-
# mail.to_s #=> "From: mikel@test.lindsaar.net\r\nTo: you@...
-
#
-
# ===Making an email via passing a string
-
#
-
# mail = Mail.new("To: mikel@test.lindsaar.net\r\nSubject: Hello\r\n\r\nHi there!")
-
# mail.body.to_s #=> 'Hi there!'
-
# mail.subject #=> 'Hello'
-
# mail.to #=> 'mikel@test.lindsaar.net'
-
#
-
# ===Making an email from a file
-
#
-
# mail = Mail.read('path/to/file.eml')
-
# mail.body.to_s #=> 'Hi there!'
-
# mail.subject #=> 'Hello'
-
# mail.to #=> 'mikel@test.lindsaar.net'
-
#
-
# ===Making an email via assignment
-
#
-
# You can assign values to a mail object via four approaches:
-
#
-
# * Message#field_name=(value)
-
# * Message#field_name(value)
-
# * Message#['field_name']=(value)
-
# * Message#[:field_name]=(value)
-
#
-
# Examples:
-
#
-
# mail = Mail.new
-
# mail['from'] = 'mikel@test.lindsaar.net'
-
# mail[:to] = 'you@test.lindsaar.net'
-
# mail.subject 'This is a test email'
-
# mail.body = 'This is a body'
-
#
-
# mail.to_s #=> "From: mikel@test.lindsaar.net\r\nTo: you@...
-
#
-
1
def initialize(*args, &block)
-
235
@body = nil
-
235
@body_raw = nil
-
235
@separate_parts = false
-
235
@text_part = nil
-
235
@html_part = nil
-
235
@errors = nil
-
235
@header = nil
-
235
@charset = 'UTF-8'
-
235
@defaulted_charset = true
-
-
235
@perform_deliveries = true
-
235
@raise_delivery_errors = true
-
-
235
@delivery_handler = nil
-
-
235
@delivery_method = Mail.delivery_method.dup
-
-
235
@transport_encoding = Mail::Encodings.get_encoding('7bit')
-
-
235
@mark_for_delete = false
-
-
235
if args.flatten.first.respond_to?(:each_pair)
-
64
init_with_hash(args.flatten.first)
-
else
-
171
init_with_string(args.flatten[0].to_s.strip)
-
end
-
-
235
if block_given?
-
instance_eval(&block)
-
end
-
-
235
self
-
end
-
-
# If you assign a delivery handler, mail will call :deliver_mail on the
-
# object you assign to delivery_handler, it will pass itself as the
-
# single argument.
-
#
-
# If you define a delivery_handler, then you are responsible for the
-
# following actions in the delivery cycle:
-
#
-
# * Appending the mail object to Mail.deliveries as you see fit.
-
# * Checking the mail.perform_deliveries flag to decide if you should
-
# actually call :deliver! the mail object or not.
-
# * Checking the mail.raise_delivery_errors flag to decide if you
-
# should raise delivery errors if they occur.
-
# * Actually calling :deliver! (with the bang) on the mail object to
-
# get it to deliver itself.
-
#
-
# A simplest implementation of a delivery_handler would be
-
#
-
# class MyObject
-
#
-
# def initialize
-
# @mail = Mail.new('To: mikel@test.lindsaar.net')
-
# @mail.delivery_handler = self
-
# end
-
#
-
# attr_accessor :mail
-
#
-
# def deliver_mail(mail)
-
# yield
-
# end
-
# end
-
#
-
# Then doing:
-
#
-
# obj = MyObject.new
-
# obj.mail.deliver
-
#
-
# Would cause Mail to call obj.deliver_mail passing itself as a parameter,
-
# which then can just yield and let Mail do it's own private do_delivery
-
# method.
-
1
attr_accessor :delivery_handler
-
-
# If set to false, mail will go through the motions of doing a delivery,
-
# but not actually call the delivery method or append the mail object to
-
# the Mail.deliveries collection. Useful for testing.
-
#
-
# Mail.deliveries.size #=> 0
-
# mail.delivery_method :smtp
-
# mail.perform_deliveries = false
-
# mail.deliver # Mail::SMTP not called here
-
# Mail.deliveries.size #=> 0
-
#
-
# If you want to test and query the Mail.deliveries collection to see what
-
# mail you sent, you should set perform_deliveries to true and use
-
# the :test mail delivery_method:
-
#
-
# Mail.deliveries.size #=> 0
-
# mail.delivery_method :test
-
# mail.perform_deliveries = true
-
# mail.deliver
-
# Mail.deliveries.size #=> 1
-
#
-
# This setting is ignored by mail (though still available as a flag) if you
-
# define a delivery_handler
-
1
attr_accessor :perform_deliveries
-
-
# If set to false, mail will silently catch and ignore any exceptions
-
# raised through attempting to deliver an email.
-
#
-
# This setting is ignored by mail (though still available as a flag) if you
-
# define a delivery_handler
-
1
attr_accessor :raise_delivery_errors
-
-
1
def register_for_delivery_notification(observer)
-
STDERR.puts("Message#register_for_delivery_notification is deprecated, please call Mail.register_observer instead")
-
Mail.register_observer(observer)
-
end
-
-
1
def inform_observers
-
37
Mail.inform_observers(self)
-
end
-
-
1
def inform_interceptors
-
39
Mail.inform_interceptors(self)
-
end
-
-
# Delivers an mail object.
-
#
-
# Examples:
-
#
-
# mail = Mail.read('file.eml')
-
# mail.deliver
-
1
def deliver
-
39
inform_interceptors
-
39
if delivery_handler
-
74
delivery_handler.deliver_mail(self) { do_delivery }
-
else
-
1
do_delivery
-
end
-
37
inform_observers
-
37
self
-
end
-
-
# This method bypasses checking perform_deliveries and raise_delivery_errors,
-
# so use with caution.
-
#
-
# It still however fires off the intercepters and calls the observers callbacks if they are defined.
-
#
-
# Returns self
-
1
def deliver!
-
inform_interceptors
-
response = delivery_method.deliver!(self)
-
inform_observers
-
delivery_method.settings[:return_response] ? response : self
-
end
-
-
1
def delivery_method(method = nil, settings = {})
-
158
unless method
-
40
@delivery_method
-
else
-
118
@delivery_method = Configuration.instance.lookup_delivery_method(method).new(settings)
-
end
-
end
-
-
1
def reply(*args, &block)
-
self.class.new.tap do |reply|
-
if message_id
-
bracketed_message_id = "<#{message_id}>"
-
reply.in_reply_to = bracketed_message_id
-
if !references.nil?
-
refs = [references].flatten.map { |r| "<#{r}>" }
-
refs << bracketed_message_id
-
reply.references = refs.join(' ')
-
elsif !in_reply_to.nil? && !in_reply_to.kind_of?(Array)
-
reply.references = "<#{in_reply_to}> #{bracketed_message_id}"
-
end
-
reply.references ||= bracketed_message_id
-
end
-
if subject
-
reply.subject = subject =~ /^Re:/i ? subject : "RE: #{subject}"
-
end
-
if reply_to || from
-
reply.to = self[reply_to ? :reply_to : :from].to_s
-
end
-
if to
-
reply.from = self[:to].formatted.first.to_s
-
end
-
-
unless args.empty?
-
if args.flatten.first.respond_to?(:each_pair)
-
reply.send(:init_with_hash, args.flatten.first)
-
else
-
reply.send(:init_with_string, args.flatten[0].to_s.strip)
-
end
-
end
-
-
if block_given?
-
reply.instance_eval(&block)
-
end
-
end
-
end
-
-
# Provides the operator needed for sort et al.
-
#
-
# Compares this mail object with another mail object, this is done by date, so an
-
# email that is older than another will appear first.
-
#
-
# Example:
-
#
-
# mail1 = Mail.new do
-
# date(Time.now)
-
# end
-
# mail2 = Mail.new do
-
# date(Time.now - 86400) # 1 day older
-
# end
-
# [mail2, mail1].sort #=> [mail2, mail1]
-
1
def <=>(other)
-
if other.nil?
-
1
-
else
-
self.date <=> other.date
-
end
-
end
-
-
# Two emails are the same if they have the same fields and body contents. One
-
# gotcha here is that Mail will insert Message-IDs when calling encoded, so doing
-
# mail1.encoded == mail2.encoded is most probably not going to return what you think
-
# as the assigned Message-IDs by Mail (if not already defined as the same) will ensure
-
# that the two objects are unique, and this comparison will ALWAYS return false.
-
#
-
# So the == operator has been defined like so: Two messages are the same if they have
-
# the same content, ignoring the Message-ID field, unless BOTH emails have a defined and
-
# different Message-ID value, then they are false.
-
#
-
# So, in practice the == operator works like this:
-
#
-
# m1 = Mail.new("Subject: Hello\r\n\r\nHello")
-
# m2 = Mail.new("Subject: Hello\r\n\r\nHello")
-
# m1 == m2 #=> true
-
#
-
# m1 = Mail.new("Subject: Hello\r\n\r\nHello")
-
# m2 = Mail.new("Message-ID: <1234@test>\r\nSubject: Hello\r\n\r\nHello")
-
# m1 == m2 #=> true
-
#
-
# m1 = Mail.new("Message-ID: <1234@test>\r\nSubject: Hello\r\n\r\nHello")
-
# m2 = Mail.new("Subject: Hello\r\n\r\nHello")
-
# m1 == m2 #=> true
-
#
-
# m1 = Mail.new("Message-ID: <1234@test>\r\nSubject: Hello\r\n\r\nHello")
-
# m2 = Mail.new("Message-ID: <1234@test>\r\nSubject: Hello\r\n\r\nHello")
-
# m1 == m2 #=> true
-
#
-
# m1 = Mail.new("Message-ID: <1234@test>\r\nSubject: Hello\r\n\r\nHello")
-
# m2 = Mail.new("Message-ID: <DIFFERENT@test>\r\nSubject: Hello\r\n\r\nHello")
-
# m1 == m2 #=> false
-
1
def ==(other)
-
8
return false unless other.respond_to?(:encoded)
-
-
8
if self.message_id && other.message_id
-
5
result = (self.encoded == other.encoded)
-
else
-
3
self_message_id, other_message_id = self.message_id, other.message_id
-
3
self.message_id, other.message_id = '<temp@test>', '<temp@test>'
-
3
result = self.encoded == other.encoded
-
3
self.message_id = "<#{self_message_id}>" if self_message_id
-
3
other.message_id = "<#{other_message_id}>" if other_message_id
-
3
result
-
end
-
end
-
-
# Provides access to the raw source of the message as it was when it
-
# was instantiated. This is set at initialization and so is untouched
-
# by the parsers or decoder / encoders
-
#
-
# Example:
-
#
-
# mail = Mail.new('This is an invalid email message')
-
# mail.raw_source #=> "This is an invalid email message"
-
1
def raw_source
-
346
@raw_source
-
end
-
-
# Sets the envelope from for the email
-
1
def set_envelope( val )
-
1
@raw_envelope = val
-
1
@envelope = Mail::Envelope.new( val )
-
end
-
-
# The raw_envelope is the From mikel@test.lindsaar.net Mon May 2 16:07:05 2009
-
# type field that you can see at the top of any email that has come
-
# from a mailbox
-
1
def raw_envelope
-
@raw_envelope
-
end
-
-
1
def envelope_from
-
@envelope ? @envelope.from : nil
-
end
-
-
1
def envelope_date
-
@envelope ? @envelope.date : nil
-
end
-
-
# Sets the header of the message object.
-
#
-
# Example:
-
#
-
# mail.header = 'To: mikel@test.lindsaar.net\r\nFrom: Bob@bob.com'
-
# mail.header #=> <#Mail::Header
-
1
def header=(value)
-
171
@header = Mail::Header.new(value, charset)
-
end
-
-
# Returns the header object of the message object. Or, if passed
-
# a parameter sets the value.
-
#
-
# Example:
-
#
-
# mail = Mail::Message.new('To: mikel\r\nFrom: you')
-
# mail.header #=> #<Mail::Header:0x13ce14 @raw_source="To: mikel\r\nFr...
-
#
-
# mail.header #=> nil
-
# mail.header 'To: mikel\r\nFrom: you'
-
# mail.header #=> #<Mail::Header:0x13ce14 @raw_source="To: mikel\r\nFr...
-
1
def header(value = nil)
-
6401
value ? self.header = value : @header
-
end
-
-
# Provides a way to set custom headers, by passing in a hash
-
1
def headers(hash = {})
-
3
hash.each_pair do |k,v|
-
4
header[k] = v
-
end
-
end
-
-
# Returns a list of parser errors on the header, each field that had an error
-
# will be reparsed as an unstructured field to preserve the data inside, but
-
# will not be used for further processing.
-
#
-
# It returns a nested array of [field_name, value, original_error_message]
-
# per error found.
-
#
-
# Example:
-
#
-
# message = Mail.new("Content-Transfer-Encoding: weirdo\r\n")
-
# message.errors.size #=> 1
-
# message.errors.first[0] #=> "Content-Transfer-Encoding"
-
# message.errors.first[1] #=> "weirdo"
-
# message.errors.first[3] #=> <The original error message exception>
-
#
-
# This is a good first defence on detecting spam by the way. Some spammers send
-
# invalid emails to try and get email parsers to give up parsing them.
-
1
def errors
-
header.errors
-
end
-
-
# Returns the Bcc value of the mail object as an array of strings of
-
# address specs.
-
#
-
# Example:
-
#
-
# mail.bcc = 'Mikel <mikel@test.lindsaar.net>'
-
# mail.bcc #=> ['mikel@test.lindsaar.net']
-
# mail.bcc = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
-
# mail.bcc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
#
-
# Also allows you to set the value by passing a value as a parameter
-
#
-
# Example:
-
#
-
# mail.bcc 'Mikel <mikel@test.lindsaar.net>'
-
# mail.bcc #=> ['mikel@test.lindsaar.net']
-
#
-
# Additionally, you can append new addresses to the returned Array like
-
# object.
-
#
-
# Example:
-
#
-
# mail.bcc 'Mikel <mikel@test.lindsaar.net>'
-
# mail.bcc << 'ada@test.lindsaar.net'
-
# mail.bcc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
1
def bcc( val = nil )
-
40
default :bcc, val
-
end
-
-
# Sets the Bcc value of the mail object, pass in a string of the field
-
#
-
# Example:
-
#
-
# mail.bcc = 'Mikel <mikel@test.lindsaar.net>'
-
# mail.bcc #=> ['mikel@test.lindsaar.net']
-
# mail.bcc = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
-
# mail.bcc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
1
def bcc=( val )
-
header[:bcc] = val
-
end
-
-
# Returns the Cc value of the mail object as an array of strings of
-
# address specs.
-
#
-
# Example:
-
#
-
# mail.cc = 'Mikel <mikel@test.lindsaar.net>'
-
# mail.cc #=> ['mikel@test.lindsaar.net']
-
# mail.cc = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
-
# mail.cc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
#
-
# Also allows you to set the value by passing a value as a parameter
-
#
-
# Example:
-
#
-
# mail.cc 'Mikel <mikel@test.lindsaar.net>'
-
# mail.cc #=> ['mikel@test.lindsaar.net']
-
#
-
# Additionally, you can append new addresses to the returned Array like
-
# object.
-
#
-
# Example:
-
#
-
# mail.cc 'Mikel <mikel@test.lindsaar.net>'
-
# mail.cc << 'ada@test.lindsaar.net'
-
# mail.cc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
1
def cc( val = nil )
-
40
default :cc, val
-
end
-
-
# Sets the Cc value of the mail object, pass in a string of the field
-
#
-
# Example:
-
#
-
# mail.cc = 'Mikel <mikel@test.lindsaar.net>'
-
# mail.cc #=> ['mikel@test.lindsaar.net']
-
# mail.cc = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
-
# mail.cc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
1
def cc=( val )
-
header[:cc] = val
-
end
-
-
1
def comments( val = nil )
-
default :comments, val
-
end
-
-
1
def comments=( val )
-
header[:comments] = val
-
end
-
-
1
def content_description( val = nil )
-
default :content_description, val
-
end
-
-
1
def content_description=( val )
-
header[:content_description] = val
-
end
-
-
1
def content_disposition( val = nil )
-
102
default :content_disposition, val
-
end
-
-
1
def content_disposition=( val )
-
header[:content_disposition] = val
-
end
-
-
1
def content_id( val = nil )
-
1
default :content_id, val
-
end
-
-
1
def content_id=( val )
-
header[:content_id] = val
-
end
-
-
1
def content_location( val = nil )
-
84
default :content_location, val
-
end
-
-
1
def content_location=( val )
-
header[:content_location] = val
-
end
-
-
1
def content_transfer_encoding( val = nil )
-
100
default :content_transfer_encoding, val
-
end
-
-
1
def content_transfer_encoding=( val )
-
84
header[:content_transfer_encoding] = val
-
end
-
-
1
def content_type( val = nil )
-
543
default :content_type, val
-
end
-
-
1
def content_type=( val )
-
122
header[:content_type] = val
-
end
-
-
1
def date( val = nil )
-
38
default :date, val
-
end
-
-
1
def date=( val )
-
1
header[:date] = val
-
end
-
-
1
def transport_encoding( val = nil)
-
14
if val
-
self.transport_encoding = val
-
else
-
14
@transport_encoding
-
end
-
end
-
-
1
def transport_encoding=( val )
-
14
@transport_encoding = Mail::Encodings.get_encoding(val)
-
end
-
-
# Returns the From value of the mail object as an array of strings of
-
# address specs.
-
#
-
# Example:
-
#
-
# mail.from = 'Mikel <mikel@test.lindsaar.net>'
-
# mail.from #=> ['mikel@test.lindsaar.net']
-
# mail.from = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
-
# mail.from #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
#
-
# Also allows you to set the value by passing a value as a parameter
-
#
-
# Example:
-
#
-
# mail.from 'Mikel <mikel@test.lindsaar.net>'
-
# mail.from #=> ['mikel@test.lindsaar.net']
-
#
-
# Additionally, you can append new addresses to the returned Array like
-
# object.
-
#
-
# Example:
-
#
-
# mail.from 'Mikel <mikel@test.lindsaar.net>'
-
# mail.from << 'ada@test.lindsaar.net'
-
# mail.from #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
1
def from( val = nil )
-
42
default :from, val
-
end
-
-
# Sets the From value of the mail object, pass in a string of the field
-
#
-
# Example:
-
#
-
# mail.from = 'Mikel <mikel@test.lindsaar.net>'
-
# mail.from #=> ['mikel@test.lindsaar.net']
-
# mail.from = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
-
# mail.from #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
1
def from=( val )
-
1
header[:from] = val
-
end
-
-
1
def in_reply_to( val = nil )
-
default :in_reply_to, val
-
end
-
-
1
def in_reply_to=( val )
-
header[:in_reply_to] = val
-
end
-
-
1
def keywords( val = nil )
-
default :keywords, val
-
end
-
-
1
def keywords=( val )
-
header[:keywords] = val
-
end
-
-
# Returns the Message-ID of the mail object. Note, per RFC 2822 the Message ID
-
# consists of what is INSIDE the < > usually seen in the mail header, so this method
-
# will return only what is inside.
-
#
-
# Example:
-
#
-
# mail.message_id = '<1234@message.id>'
-
# mail.message_id #=> '1234@message.id'
-
#
-
# Also allows you to set the Message-ID by passing a string as a parameter
-
#
-
# mail.message_id '<1234@message.id>'
-
# mail.message_id #=> '1234@message.id'
-
1
def message_id( val = nil )
-
56
default :message_id, val
-
end
-
-
# Sets the Message-ID. Note, per RFC 2822 the Message ID consists of what is INSIDE
-
# the < > usually seen in the mail header, so this method will return only what is inside.
-
#
-
# mail.message_id = '<1234@message.id>'
-
# mail.message_id #=> '1234@message.id'
-
1
def message_id=( val )
-
9
header[:message_id] = val
-
end
-
-
# Returns the MIME version of the email as a string
-
#
-
# Example:
-
#
-
# mail.mime_version = '1.0'
-
# mail.mime_version #=> '1.0'
-
#
-
# Also allows you to set the MIME version by passing a string as a parameter.
-
#
-
# Example:
-
#
-
# mail.mime_version '1.0'
-
# mail.mime_version #=> '1.0'
-
1
def mime_version( val = nil )
-
4
default :mime_version, val
-
end
-
-
# Sets the MIME version of the email by accepting a string
-
#
-
# Example:
-
#
-
# mail.mime_version = '1.0'
-
# mail.mime_version #=> '1.0'
-
1
def mime_version=( val )
-
44
header[:mime_version] = val
-
end
-
-
1
def received( val = nil )
-
if val
-
header[:received] = val
-
else
-
header[:received]
-
end
-
end
-
-
1
def received=( val )
-
header[:received] = val
-
end
-
-
1
def references( val = nil )
-
default :references, val
-
end
-
-
1
def references=( val )
-
header[:references] = val
-
end
-
-
# Returns the Reply-To value of the mail object as an array of strings of
-
# address specs.
-
#
-
# Example:
-
#
-
# mail.reply_to = 'Mikel <mikel@test.lindsaar.net>'
-
# mail.reply_to #=> ['mikel@test.lindsaar.net']
-
# mail.reply_to = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
-
# mail.reply_to #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
#
-
# Also allows you to set the value by passing a value as a parameter
-
#
-
# Example:
-
#
-
# mail.reply_to 'Mikel <mikel@test.lindsaar.net>'
-
# mail.reply_to #=> ['mikel@test.lindsaar.net']
-
#
-
# Additionally, you can append new addresses to the returned Array like
-
# object.
-
#
-
# Example:
-
#
-
# mail.reply_to 'Mikel <mikel@test.lindsaar.net>'
-
# mail.reply_to << 'ada@test.lindsaar.net'
-
# mail.reply_to #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
1
def reply_to( val = nil )
-
1
default :reply_to, val
-
end
-
-
# Sets the Reply-To value of the mail object, pass in a string of the field
-
#
-
# Example:
-
#
-
# mail.reply_to = 'Mikel <mikel@test.lindsaar.net>'
-
# mail.reply_to #=> ['mikel@test.lindsaar.net']
-
# mail.reply_to = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
-
# mail.reply_to #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
1
def reply_to=( val )
-
header[:reply_to] = val
-
end
-
-
# Returns the Resent-Bcc value of the mail object as an array of strings of
-
# address specs.
-
#
-
# Example:
-
#
-
# mail.resent_bcc = 'Mikel <mikel@test.lindsaar.net>'
-
# mail.resent_bcc #=> ['mikel@test.lindsaar.net']
-
# mail.resent_bcc = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
-
# mail.resent_bcc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
#
-
# Also allows you to set the value by passing a value as a parameter
-
#
-
# Example:
-
#
-
# mail.resent_bcc 'Mikel <mikel@test.lindsaar.net>'
-
# mail.resent_bcc #=> ['mikel@test.lindsaar.net']
-
#
-
# Additionally, you can append new addresses to the returned Array like
-
# object.
-
#
-
# Example:
-
#
-
# mail.resent_bcc 'Mikel <mikel@test.lindsaar.net>'
-
# mail.resent_bcc << 'ada@test.lindsaar.net'
-
# mail.resent_bcc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
1
def resent_bcc( val = nil )
-
default :resent_bcc, val
-
end
-
-
# Sets the Resent-Bcc value of the mail object, pass in a string of the field
-
#
-
# Example:
-
#
-
# mail.resent_bcc = 'Mikel <mikel@test.lindsaar.net>'
-
# mail.resent_bcc #=> ['mikel@test.lindsaar.net']
-
# mail.resent_bcc = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
-
# mail.resent_bcc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
1
def resent_bcc=( val )
-
header[:resent_bcc] = val
-
end
-
-
# Returns the Resent-Cc value of the mail object as an array of strings of
-
# address specs.
-
#
-
# Example:
-
#
-
# mail.resent_cc = 'Mikel <mikel@test.lindsaar.net>'
-
# mail.resent_cc #=> ['mikel@test.lindsaar.net']
-
# mail.resent_cc = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
-
# mail.resent_cc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
#
-
# Also allows you to set the value by passing a value as a parameter
-
#
-
# Example:
-
#
-
# mail.resent_cc 'Mikel <mikel@test.lindsaar.net>'
-
# mail.resent_cc #=> ['mikel@test.lindsaar.net']
-
#
-
# Additionally, you can append new addresses to the returned Array like
-
# object.
-
#
-
# Example:
-
#
-
# mail.resent_cc 'Mikel <mikel@test.lindsaar.net>'
-
# mail.resent_cc << 'ada@test.lindsaar.net'
-
# mail.resent_cc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
1
def resent_cc( val = nil )
-
default :resent_cc, val
-
end
-
-
# Sets the Resent-Cc value of the mail object, pass in a string of the field
-
#
-
# Example:
-
#
-
# mail.resent_cc = 'Mikel <mikel@test.lindsaar.net>'
-
# mail.resent_cc #=> ['mikel@test.lindsaar.net']
-
# mail.resent_cc = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
-
# mail.resent_cc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
1
def resent_cc=( val )
-
header[:resent_cc] = val
-
end
-
-
1
def resent_date( val = nil )
-
default :resent_date, val
-
end
-
-
1
def resent_date=( val )
-
header[:resent_date] = val
-
end
-
-
# Returns the Resent-From value of the mail object as an array of strings of
-
# address specs.
-
#
-
# Example:
-
#
-
# mail.resent_from = 'Mikel <mikel@test.lindsaar.net>'
-
# mail.resent_from #=> ['mikel@test.lindsaar.net']
-
# mail.resent_from = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
-
# mail.resent_from #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
#
-
# Also allows you to set the value by passing a value as a parameter
-
#
-
# Example:
-
#
-
# mail.resent_from ['Mikel <mikel@test.lindsaar.net>']
-
# mail.resent_from #=> 'mikel@test.lindsaar.net'
-
#
-
# Additionally, you can append new addresses to the returned Array like
-
# object.
-
#
-
# Example:
-
#
-
# mail.resent_from 'Mikel <mikel@test.lindsaar.net>'
-
# mail.resent_from << 'ada@test.lindsaar.net'
-
# mail.resent_from #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
1
def resent_from( val = nil )
-
default :resent_from, val
-
end
-
-
# Sets the Resent-From value of the mail object, pass in a string of the field
-
#
-
# Example:
-
#
-
# mail.resent_from = 'Mikel <mikel@test.lindsaar.net>'
-
# mail.resent_from #=> ['mikel@test.lindsaar.net']
-
# mail.resent_from = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
-
# mail.resent_from #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
1
def resent_from=( val )
-
header[:resent_from] = val
-
end
-
-
1
def resent_message_id( val = nil )
-
default :resent_message_id, val
-
end
-
-
1
def resent_message_id=( val )
-
header[:resent_message_id] = val
-
end
-
-
# Returns the Resent-Sender value of the mail object, as a single string of an address
-
# spec. A sender per RFC 2822 must be a single address, so you can not append to
-
# this address.
-
#
-
# Example:
-
#
-
# mail.resent_sender = 'Mikel <mikel@test.lindsaar.net>'
-
# mail.resent_sender #=> 'mikel@test.lindsaar.net'
-
#
-
# Also allows you to set the value by passing a value as a parameter
-
#
-
# Example:
-
#
-
# mail.resent_sender 'Mikel <mikel@test.lindsaar.net>'
-
# mail.resent_sender #=> 'mikel@test.lindsaar.net'
-
1
def resent_sender( val = nil )
-
default :resent_sender, val
-
end
-
-
# Sets the Resent-Sender value of the mail object, pass in a string of the field
-
#
-
# Example:
-
#
-
# mail.sender = 'Mikel <mikel@test.lindsaar.net>'
-
# mail.sender #=> 'mikel@test.lindsaar.net'
-
1
def resent_sender=( val )
-
header[:resent_sender] = val
-
end
-
-
# Returns the Resent-To value of the mail object as an array of strings of
-
# address specs.
-
#
-
# Example:
-
#
-
# mail.resent_to = 'Mikel <mikel@test.lindsaar.net>'
-
# mail.resent_to #=> ['mikel@test.lindsaar.net']
-
# mail.resent_to = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
-
# mail.resent_to #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
#
-
# Also allows you to set the value by passing a value as a parameter
-
#
-
# Example:
-
#
-
# mail.resent_to 'Mikel <mikel@test.lindsaar.net>'
-
# mail.resent_to #=> ['mikel@test.lindsaar.net']
-
#
-
# Additionally, you can append new addresses to the returned Array like
-
# object.
-
#
-
# Example:
-
#
-
# mail.resent_to 'Mikel <mikel@test.lindsaar.net>'
-
# mail.resent_to << 'ada@test.lindsaar.net'
-
# mail.resent_to #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
1
def resent_to( val = nil )
-
default :resent_to, val
-
end
-
-
# Sets the Resent-To value of the mail object, pass in a string of the field
-
#
-
# Example:
-
#
-
# mail.resent_to = 'Mikel <mikel@test.lindsaar.net>'
-
# mail.resent_to #=> ['mikel@test.lindsaar.net']
-
# mail.resent_to = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
-
# mail.resent_to #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
1
def resent_to=( val )
-
header[:resent_to] = val
-
end
-
-
# Returns the return path of the mail object, or sets it if you pass a string
-
1
def return_path( val = nil )
-
1
default :return_path, val
-
end
-
-
# Sets the return path of the object
-
1
def return_path=( val )
-
header[:return_path] = val
-
end
-
-
# Returns the Sender value of the mail object, as a single string of an address
-
# spec. A sender per RFC 2822 must be a single address.
-
#
-
# Example:
-
#
-
# mail.sender = 'Mikel <mikel@test.lindsaar.net>'
-
# mail.sender #=> 'mikel@test.lindsaar.net'
-
#
-
# Also allows you to set the value by passing a value as a parameter
-
#
-
# Example:
-
#
-
# mail.sender 'Mikel <mikel@test.lindsaar.net>'
-
# mail.sender #=> 'mikel@test.lindsaar.net'
-
1
def sender( val = nil )
-
1
default :sender, val
-
end
-
-
# Sets the Sender value of the mail object, pass in a string of the field
-
#
-
# Example:
-
#
-
# mail.sender = 'Mikel <mikel@test.lindsaar.net>'
-
# mail.sender #=> 'mikel@test.lindsaar.net'
-
1
def sender=( val )
-
header[:sender] = val
-
end
-
-
# Returns the decoded value of the subject field, as a single string.
-
#
-
# Example:
-
#
-
# mail.subject = "G'Day mate"
-
# mail.subject #=> "G'Day mate"
-
# mail.subject = '=?UTF-8?Q?This_is_=E3=81=82_string?='
-
# mail.subject #=> "This is あ string"
-
#
-
# Also allows you to set the value by passing a value as a parameter
-
#
-
# Example:
-
#
-
# mail.subject "G'Day mate"
-
# mail.subject #=> "G'Day mate"
-
1
def subject( val = nil )
-
46
default :subject, val
-
end
-
-
# Sets the Subject value of the mail object, pass in a string of the field
-
#
-
# Example:
-
#
-
# mail.subject = '=?UTF-8?Q?This_is_=E3=81=82_string?='
-
# mail.subject #=> "This is あ string"
-
1
def subject=( val )
-
1
header[:subject] = val
-
end
-
-
# Returns the To value of the mail object as an array of strings of
-
# address specs.
-
#
-
# Example:
-
#
-
# mail.to = 'Mikel <mikel@test.lindsaar.net>'
-
# mail.to #=> ['mikel@test.lindsaar.net']
-
# mail.to = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
-
# mail.to #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
#
-
# Also allows you to set the value by passing a value as a parameter
-
#
-
# Example:
-
#
-
# mail.to 'Mikel <mikel@test.lindsaar.net>'
-
# mail.to #=> ['mikel@test.lindsaar.net']
-
#
-
# Additionally, you can append new addresses to the returned Array like
-
# object.
-
#
-
# Example:
-
#
-
# mail.to 'Mikel <mikel@test.lindsaar.net>'
-
# mail.to << 'ada@test.lindsaar.net'
-
# mail.to #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
1
def to( val = nil )
-
43
default :to, val
-
end
-
-
# Sets the To value of the mail object, pass in a string of the field
-
#
-
# Example:
-
#
-
# mail.to = 'Mikel <mikel@test.lindsaar.net>'
-
# mail.to #=> ['mikel@test.lindsaar.net']
-
# mail.to = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
-
# mail.to #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
1
def to=( val )
-
1
header[:to] = val
-
end
-
-
# Returns the default value of the field requested as a symbol.
-
#
-
# Each header field has a :default method which returns the most common use case for
-
# that field, for example, the date field types will return a DateTime object when
-
# sent :default, the subject, or unstructured fields will return a decoded string of
-
# their value, the address field types will return a single addr_spec or an array of
-
# addr_specs if there is more than one.
-
1
def default( sym, val = nil )
-
1142
if val
-
44
header[sym] = val
-
else
-
1098
header[sym].default if header[sym]
-
end
-
end
-
-
# Sets the body object of the message object.
-
#
-
# Example:
-
#
-
# mail.body = 'This is the body'
-
# mail.body #=> #<Mail::Body:0x13919c @raw_source="This is the bo...
-
#
-
# You can also reset the body of an Message object by setting body to nil
-
#
-
# Example:
-
#
-
# mail.body = 'this is the body'
-
# mail.body.encoded #=> 'this is the body'
-
# mail.body = nil
-
# mail.body.encoded #=> ''
-
#
-
# If you try and set the body of an email that is a multipart email, then instead
-
# of deleting all the parts of your email, mail will add a text/plain part to
-
# your email:
-
#
-
# mail.add_file 'somefilename.png'
-
# mail.parts.length #=> 1
-
# mail.body = "This is a body"
-
# mail.parts.length #=> 2
-
# mail.parts.last.content_type.content_type #=> 'This is a body'
-
1
def body=(value)
-
323
body_lazy(value)
-
end
-
-
# Returns the body of the message object. Or, if passed
-
# a parameter sets the value.
-
#
-
# Example:
-
#
-
# mail = Mail::Message.new('To: mikel\r\n\r\nThis is the body')
-
# mail.body #=> #<Mail::Body:0x13919c @raw_source="This is the bo...
-
#
-
# mail.body 'This is another body'
-
# mail.body #=> #<Mail::Body:0x13919c @raw_source="This is anothe...
-
1
def body(value = nil)
-
1620
if value
-
self.body = value
-
# add_encoding_to_body
-
else
-
1620
process_body_raw if @body_raw
-
1620
@body
-
end
-
end
-
-
1
def body_encoding(value)
-
if value.nil?
-
body.encoding
-
else
-
body.encoding = value
-
end
-
end
-
-
1
def body_encoding=(value)
-
body.encoding = value
-
end
-
-
# Returns the list of addresses this message should be sent to by
-
# collecting the addresses off the to, cc and bcc fields.
-
#
-
# Example:
-
#
-
# mail.to = 'mikel@test.lindsaar.net'
-
# mail.cc = 'sam@test.lindsaar.net'
-
# mail.bcc = 'bob@test.lindsaar.net'
-
# mail.destinations.length #=> 3
-
# mail.destinations.first #=> 'mikel@test.lindsaar.net'
-
1
def destinations
-
2
[to_addrs, cc_addrs, bcc_addrs].compact.flatten
-
end
-
-
# Returns an array of addresses (the encoded value) in the From field,
-
# if no From field, returns an empty array
-
1
def from_addrs
-
1
from ? [from].flatten : []
-
end
-
-
# Returns an array of addresses (the encoded value) in the To field,
-
# if no To field, returns an empty array
-
1
def to_addrs
-
2
to ? [to].flatten : []
-
end
-
-
# Returns an array of addresses (the encoded value) in the Cc field,
-
# if no Cc field, returns an empty array
-
1
def cc_addrs
-
2
cc ? [cc].flatten : []
-
end
-
-
# Returns an array of addresses (the encoded value) in the Bcc field,
-
# if no Bcc field, returns an empty array
-
1
def bcc_addrs
-
2
bcc ? [bcc].flatten : []
-
end
-
-
# Allows you to add an arbitrary header
-
#
-
# Example:
-
#
-
# mail['foo'] = '1234'
-
# mail['foo'].to_s #=> '1234'
-
1
def []=(name, value)
-
994
if name.to_s == 'body'
-
87
self.body = value
-
907
elsif name.to_s =~ /content[-_]type/i
-
151
header[name] = value
-
756
elsif name.to_s == 'charset'
-
170
self.charset = value
-
else
-
586
header[name] = value
-
end
-
end
-
-
# Allows you to read an arbitrary header
-
#
-
# Example:
-
#
-
# mail['foo'] = '1234'
-
# mail['foo'].to_s #=> '1234'
-
1
def [](name)
-
185
header[underscoreize(name)]
-
end
-
-
# Method Missing in this implementation allows you to set any of the
-
# standard fields directly as you would the "to", "subject" etc.
-
#
-
# Those fields used most often (to, subject et al) are given their
-
# own method for ease of documentation and also to avoid the hook
-
# call to method missing.
-
#
-
# This will only catch the known fields listed in:
-
#
-
# Mail::Field::KNOWN_FIELDS
-
#
-
# as per RFC 2822, any ruby string or method name could pretty much
-
# be a field name, so we don't want to just catch ANYTHING sent to
-
# a message object and interpret it as a header.
-
#
-
# This method provides all three types of header call to set, read
-
# and explicitly set with the = operator
-
#
-
# Examples:
-
#
-
# mail.comments = 'These are some comments'
-
# mail.comments #=> 'These are some comments'
-
#
-
# mail.comments 'These are other comments'
-
# mail.comments #=> 'These are other comments'
-
#
-
#
-
# mail.date = 'Tue, 1 Jul 2003 10:52:37 +0200'
-
# mail.date.to_s #=> 'Tue, 1 Jul 2003 10:52:37 +0200'
-
#
-
# mail.date 'Tue, 1 Jul 2003 10:52:37 +0200'
-
# mail.date.to_s #=> 'Tue, 1 Jul 2003 10:52:37 +0200'
-
#
-
#
-
# mail.resent_msg_id = '<1234@resent_msg_id.lindsaar.net>'
-
# mail.resent_msg_id #=> '<1234@resent_msg_id.lindsaar.net>'
-
#
-
# mail.resent_msg_id '<4567@resent_msg_id.lindsaar.net>'
-
# mail.resent_msg_id #=> '<4567@resent_msg_id.lindsaar.net>'
-
1
def method_missing(name, *args, &block)
-
#:nodoc:
-
# Only take the structured fields, as we could take _anything_ really
-
# as it could become an optional field... "but therin lies the dark side"
-
61
field_name = underscoreize(name).chomp("=")
-
61
if Mail::Field::KNOWN_FIELDS.include?(field_name)
-
if args.empty?
-
header[field_name]
-
else
-
header[field_name] = args.first
-
end
-
else
-
61
super # otherwise pass it on
-
end
-
#:startdoc:
-
end
-
-
# Returns an FieldList of all the fields in the header in the order that
-
# they appear in the header
-
1
def header_fields
-
header.fields
-
end
-
-
# Returns true if the message has a message ID field, the field may or may
-
# not have a value, but the field exists or not.
-
1
def has_message_id?
-
84
header.has_message_id?
-
end
-
-
# Returns true if the message has a Date field, the field may or may
-
# not have a value, but the field exists or not.
-
1
def has_date?
-
84
header.has_date?
-
end
-
-
# Returns true if the message has a Date field, the field may or may
-
# not have a value, but the field exists or not.
-
1
def has_mime_version?
-
84
header.has_mime_version?
-
end
-
-
1
def has_content_type?
-
1039
tmp = header[:content_type].main_type rescue nil
-
1039
!!tmp
-
end
-
-
1
def has_charset?
-
84
tmp = header[:content_type].parameters rescue nil
-
84
!!(has_content_type? && tmp && tmp['charset'])
-
end
-
-
1
def has_content_transfer_encoding?
-
470
header[:content_transfer_encoding] && header[:content_transfer_encoding].errors.blank?
-
end
-
-
1
def has_transfer_encoding? # :nodoc:
-
STDERR.puts(":has_transfer_encoding? is deprecated in Mail 1.4.3. Please use has_content_transfer_encoding?\n#{caller}")
-
has_content_transfer_encoding?
-
end
-
-
# Creates a new empty Message-ID field and inserts it in the correct order
-
# into the Header. The MessageIdField object will automatically generate
-
# a unique message ID if you try and encode it or output it to_s without
-
# specifying a message id.
-
#
-
# It will preserve the message ID you specify if you do.
-
1
def add_message_id(msg_id_val = '')
-
35
header['message-id'] = msg_id_val
-
end
-
-
# Creates a new empty Date field and inserts it in the correct order
-
# into the Header. The DateField object will automatically generate
-
# DateTime.now's date if you try and encode it or output it to_s without
-
# specifying a date yourself.
-
#
-
# It will preserve any date you specify if you do.
-
1
def add_date(date_val = '')
-
48
header['date'] = date_val
-
end
-
-
# Creates a new empty Mime Version field and inserts it in the correct order
-
# into the Header. The MimeVersion object will automatically generate
-
# set itself to '1.0' if you try and encode it or output it to_s without
-
# specifying a version yourself.
-
#
-
# It will preserve any date you specify if you do.
-
1
def add_mime_version(ver_val = '')
-
12
header['mime-version'] = ver_val
-
end
-
-
# Adds a content type and charset if the body is US-ASCII
-
#
-
# Otherwise raises a warning
-
1
def add_content_type
-
header[:content_type] = 'text/plain'
-
end
-
-
# Adds a content type and charset if the body is US-ASCII
-
#
-
# Otherwise raises a warning
-
1
def add_charset
-
2
if !body.empty?
-
# Only give a warning if this isn't an attachment, has non US-ASCII and the user
-
# has not specified an encoding explicitly.
-
2
if @defaulted_charset && body.raw_source.not_ascii_only? && !self.attachment?
-
warning = "Non US-ASCII detected and no charset defined.\nDefaulting to UTF-8, set your own if this is incorrect.\n"
-
STDERR.puts(warning)
-
end
-
2
header[:content_type].parameters['charset'] = @charset
-
end
-
end
-
-
# Adds a content transfer encoding
-
#
-
# Otherwise raises a warning
-
1
def add_content_transfer_encoding
-
if body.only_us_ascii?
-
header[:content_transfer_encoding] = '7bit'
-
else
-
warning = "Non US-ASCII detected and no content-transfer-encoding defined.\nDefaulting to 8bit, set your own if this is incorrect.\n"
-
STDERR.puts(warning)
-
header[:content_transfer_encoding] = '8bit'
-
end
-
end
-
-
1
def add_transfer_encoding # :nodoc:
-
STDERR.puts(":add_transfer_encoding is deprecated in Mail 1.4.3. Please use add_content_transfer_encoding\n#{caller}")
-
add_content_transfer_encoding
-
end
-
-
1
def transfer_encoding # :nodoc:
-
STDERR.puts(":transfer_encoding is deprecated in Mail 1.4.3. Please use content_transfer_encoding\n#{caller}")
-
content_transfer_encoding
-
end
-
-
# Returns the MIME media type of part we are on, this is taken from the content-type header
-
1
def mime_type
-
77
content_type ? header[:content_type].string : nil rescue nil
-
end
-
-
1
def message_content_type
-
STDERR.puts(":message_content_type is deprecated in Mail 1.4.3. Please use mime_type\n#{caller}")
-
mime_type
-
end
-
-
# Returns the character set defined in the content type field
-
1
def charset
-
226
if @header
-
55
content_type ? content_type_parameters['charset'] : @charset
-
else
-
171
@charset
-
end
-
end
-
-
# Sets the charset to the supplied value.
-
1
def charset=(value)
-
406
@defaulted_charset = false
-
406
@charset = value
-
406
@header.charset = value
-
end
-
-
# Returns the main content type
-
1
def main_type
-
227
has_content_type? ? header[:content_type].main_type : nil rescue nil
-
end
-
-
# Returns the sub content type
-
1
def sub_type
-
has_content_type? ? header[:content_type].sub_type : nil rescue nil
-
end
-
-
# Returns the content type parameters
-
1
def mime_parameters
-
STDERR.puts(':mime_parameters is deprecated in Mail 1.4.3, please use :content_type_parameters instead')
-
content_type_parameters
-
end
-
-
# Returns the content type parameters
-
1
def content_type_parameters
-
251
has_content_type? ? header[:content_type].parameters : nil rescue nil
-
end
-
-
# Returns true if the message is multipart
-
1
def multipart?
-
390
has_content_type? ? !!(main_type =~ /^multipart$/i) : false
-
end
-
-
# Returns true if the message is a multipart/report
-
1
def multipart_report?
-
multipart? && sub_type =~ /^report$/i
-
end
-
-
# Returns true if the message is a multipart/report; report-type=delivery-status;
-
1
def delivery_status_report?
-
multipart_report? && content_type_parameters['report-type'] =~ /^delivery-status$/i
-
end
-
-
# returns the part in a multipart/report email that has the content-type delivery-status
-
1
def delivery_status_part
-
@delivery_stats_part ||= parts.select { |p| p.delivery_status_report_part? }.first
-
end
-
-
1
def bounced?
-
delivery_status_part and delivery_status_part.bounced?
-
end
-
-
1
def action
-
delivery_status_part and delivery_status_part.action
-
end
-
-
1
def final_recipient
-
delivery_status_part and delivery_status_part.final_recipient
-
end
-
-
1
def error_status
-
delivery_status_part and delivery_status_part.error_status
-
end
-
-
1
def diagnostic_code
-
delivery_status_part and delivery_status_part.diagnostic_code
-
end
-
-
1
def remote_mta
-
delivery_status_part and delivery_status_part.remote_mta
-
end
-
-
1
def retryable?
-
delivery_status_part and delivery_status_part.retryable?
-
end
-
-
# Returns the current boundary for this message part
-
1
def boundary
-
56
content_type_parameters ? content_type_parameters['boundary'] : nil
-
end
-
-
# Returns a parts list object of all the parts in the message
-
1
def parts
-
699
body.parts
-
end
-
-
# Returns an AttachmentsList object, which holds all of the attachments in
-
# the receiver object (either the entier email or a part within) and all
-
# of it's descendants.
-
#
-
# It also allows you to add attachments to the mail object directly, like so:
-
#
-
# mail.attachments['filename.jpg'] = File.read('/path/to/filename.jpg')
-
#
-
# If you do this, then Mail will take the file name and work out the MIME media type
-
# set the Content-Type, Content-Disposition, Content-Transfer-Encoding and
-
# base64 encode the contents of the attachment all for you.
-
#
-
# You can also specify overrides if you want by passing a hash instead of a string:
-
#
-
# mail.attachments['filename.jpg'] = {:mime_type => 'application/x-gzip',
-
# :content => File.read('/path/to/filename.jpg')}
-
#
-
# If you want to use a different encoding than Base64, you can pass an encoding in,
-
# but then it is up to you to pass in the content pre-encoded, and don't expect
-
# Mail to know how to decode this data:
-
#
-
# file_content = SpecialEncode(File.read('/path/to/filename.jpg'))
-
# mail.attachments['filename.jpg'] = {:mime_type => 'application/x-gzip',
-
# :encoding => 'SpecialEncoding',
-
# :content => file_content }
-
#
-
# You can also search for specific attachments:
-
#
-
# # By Filename
-
# mail.attachments['filename.jpg'] #=> Mail::Part object or nil
-
#
-
# # or by index
-
# mail.attachments[0] #=> Mail::Part (first attachment)
-
#
-
1
def attachments
-
276
parts.attachments
-
end
-
-
1
def has_attachments?
-
231
!attachments.empty?
-
end
-
-
# Accessor for html_part
-
1
def html_part(&block)
-
3
if block_given?
-
@html_part = Mail::Part.new(&block)
-
add_multipart_alternate_header unless html_part.blank?
-
add_part(@html_part)
-
else
-
3
@html_part || find_first_mime_type('text/html')
-
end
-
end
-
-
# Accessor for text_part
-
1
def text_part(&block)
-
3
if block_given?
-
@text_part = Mail::Part.new(&block)
-
add_multipart_alternate_header unless html_part.blank?
-
add_part(@text_part)
-
else
-
3
@text_part || find_first_mime_type('text/plain')
-
end
-
end
-
-
# Helper to add a html part to a multipart/alternative email. If this and
-
# text_part are both defined in a message, then it will be a multipart/alternative
-
# message and set itself that way.
-
1
def html_part=(msg = nil)
-
if msg
-
@html_part = msg
-
else
-
@html_part = Mail::Part.new('Content-Type: text/html;')
-
end
-
add_multipart_alternate_header unless text_part.blank?
-
add_part(@html_part)
-
end
-
-
# Helper to add a text part to a multipart/alternative email. If this and
-
# html_part are both defined in a message, then it will be a multipart/alternative
-
# message and set itself that way.
-
1
def text_part=(msg = nil)
-
if msg
-
@text_part = msg
-
else
-
@text_part = Mail::Part.new('Content-Type: text/plain;')
-
end
-
add_multipart_alternate_header unless html_part.blank?
-
add_part(@text_part)
-
end
-
-
# Adds a part to the parts list or creates the part list
-
1
def add_part(part)
-
56
if !body.multipart? && !self.body.decoded.blank?
-
@text_part = Mail::Part.new('Content-Type: text/plain;')
-
@text_part.body = body.decoded
-
self.body << @text_part
-
add_multipart_alternate_header
-
end
-
56
add_boundary
-
56
self.body << part
-
end
-
-
# Allows you to add a part in block form to an existing mail message object
-
#
-
# Example:
-
#
-
# mail = Mail.new do
-
# part :content_type => "multipart/alternative", :content_disposition => "inline" do |p|
-
# p.part :content_type => "text/plain", :body => "test text\nline #2"
-
# p.part :content_type => "text/html", :body => "<b>test</b> HTML<br/>\nline #2"
-
# end
-
# end
-
1
def part(params = {})
-
new_part = Part.new(params)
-
yield new_part if block_given?
-
add_part(new_part)
-
end
-
-
# Adds a file to the message. You have two options with this method, you can
-
# just pass in the absolute path to the file you want and Mail will read the file,
-
# get the filename from the path you pass in and guess the MIME media type, or you
-
# can pass in the filename as a string, and pass in the file content as a blob.
-
#
-
# Example:
-
#
-
# m = Mail.new
-
# m.add_file('/path/to/filename.png')
-
#
-
# m = Mail.new
-
# m.add_file(:filename => 'filename.png', :content => File.read('/path/to/file.jpg'))
-
#
-
# Note also that if you add a file to an existing message, Mail will convert that message
-
# to a MIME multipart email, moving whatever plain text body you had into it's own text
-
# plain part.
-
#
-
# Example:
-
#
-
# m = Mail.new do
-
# body 'this is some text'
-
# end
-
# m.multipart? #=> false
-
# m.add_file('/path/to/filename.png')
-
# m.multipart? #=> true
-
# m.parts.first.content_type.content_type #=> 'text/plain'
-
# m.parts.last.content_type.content_type #=> 'image/png'
-
#
-
# See also #attachments
-
1
def add_file(values)
-
convert_to_multipart unless self.multipart? || self.body.decoded.blank?
-
add_multipart_mixed_header
-
if values.is_a?(String)
-
basename = File.basename(values)
-
filedata = File.open(values, 'rb') { |f| f.read }
-
else
-
basename = values[:filename]
-
filedata = values[:content] || File.open(values[:filename], 'rb') { |f| f.read }
-
end
-
self.attachments[basename] = filedata
-
end
-
-
1
def convert_to_multipart
-
text = body.decoded
-
self.body = ''
-
text_part = Mail::Part.new({:content_type => 'text/plain;',
-
:body => text})
-
text_part.charset = charset unless @defaulted_charset
-
self.body << text_part
-
end
-
-
# Encodes the message, calls encode on all it's parts, gets an email message
-
# ready to send
-
1
def ready_to_send!
-
84
identify_and_set_transfer_encoding
-
84
parts.sort!([ "text/plain", "text/enriched", "text/html", "multipart/alternative" ])
-
84
parts.each do |part|
-
14
part.transport_encoding = transport_encoding
-
14
part.ready_to_send!
-
end
-
84
add_required_fields
-
end
-
-
1
def encode!
-
STDERR.puts("Deprecated in 1.1.0 in favour of :ready_to_send! as it is less confusing with encoding and decoding.")
-
ready_to_send!
-
end
-
-
# Outputs an encoded string representation of the mail message including
-
# all headers, attachments, etc. This is an encoded email in US-ASCII,
-
# so it is able to be directly sent to an email server.
-
1
def encoded
-
69
ready_to_send!
-
69
buffer = header.encoded
-
69
buffer << "\r\n"
-
69
buffer << body.encoded(content_transfer_encoding)
-
69
buffer
-
end
-
-
1
def without_attachments!
-
return self unless has_attachments?
-
-
parts.delete_if { |p| p.attachment? }
-
body_raw = if parts.empty?
-
''
-
else
-
body.encoded
-
end
-
-
@body = Mail::Body.new(body_raw)
-
-
self
-
end
-
-
1
def to_yaml(opts = {})
-
hash = {}
-
hash['headers'] = {}
-
header.fields.each do |field|
-
hash['headers'][field.name] = field.value
-
end
-
hash['delivery_handler'] = delivery_handler.to_s if delivery_handler
-
hash['transport_encoding'] = transport_encoding.to_s
-
special_variables = [:@header, :@delivery_handler, :@transport_encoding]
-
if multipart?
-
hash['multipart_body'] = []
-
body.parts.map { |part| hash['multipart_body'] << part.to_yaml }
-
special_variables.push(:@body, :@text_part, :@html_part)
-
end
-
(instance_variables.map(&:to_sym) - special_variables).each do |var|
-
hash[var.to_s] = instance_variable_get(var)
-
end
-
hash.to_yaml(opts)
-
end
-
-
1
def self.from_yaml(str)
-
hash = YAML.load(str)
-
m = self.new(:headers => hash['headers'])
-
hash.delete('headers')
-
hash.each do |k,v|
-
case
-
when k == 'delivery_handler'
-
begin
-
m.delivery_handler = Object.const_get(v) unless v.blank?
-
rescue NameError
-
end
-
when k == 'transport_encoding'
-
m.transport_encoding(v)
-
when k == 'multipart_body'
-
v.map {|part| m.add_part Mail::Part.from_yaml(part) }
-
when k =~ /^@/
-
m.instance_variable_set(k.to_sym, v)
-
end
-
end
-
m
-
end
-
-
1
def self.from_hash(hash)
-
Mail::Message.new(hash)
-
end
-
-
1
def to_s
-
encoded
-
end
-
-
1
def inspect
-
"#<#{self.class}:#{self.object_id}, Multipart: #{multipart?}, Headers: #{header.field_summary}>"
-
end
-
-
1
def decoded
-
case
-
when self.text?
-
decode_body_as_text
-
when self.attachment?
-
3
decode_body
-
when !self.multipart?
-
body.decoded
-
else
-
raise NoMethodError, 'Can not decode an entire message, try calling #decoded on the various fields and body or parts if it is a multipart message.'
-
3
end
-
end
-
-
1
def read
-
if self.attachment?
-
decode_body
-
else
-
raise NoMethodError, 'Can not call read on a part unless it is an attachment.'
-
end
-
end
-
-
1
def decode_body
-
3
body.decoded
-
end
-
-
# Returns true if this part is an attachment,
-
# false otherwise.
-
1
def attachment?
-
137
!!find_attachment
-
end
-
-
# Returns the attachment data if there is any
-
1
def attachment
-
@attachment
-
end
-
-
# Returns the filename of the attachment
-
1
def filename
-
9
find_attachment
-
end
-
-
1
def all_parts
-
30
parts.map { |p| [p, p.all_parts] }.flatten
-
end
-
-
1
def find_first_mime_type(mt)
-
15
all_parts.detect { |p| p.mime_type == mt && !p.attachment? }
-
end
-
-
# Skips the deletion of this message. All other messages
-
# flagged for delete still will be deleted at session close (i.e. when
-
# #find exits). Only has an effect if you're using #find_and_delete
-
# or #find with :delete_after_find set to true.
-
1
def skip_deletion
-
@mark_for_delete = false
-
end
-
-
# Sets whether this message should be deleted at session close (i.e.
-
# after #find). Message will only be deleted if messages are retrieved
-
# using the #find_and_delete method, or by calling #find with
-
# :delete_after_find set to true.
-
1
def mark_for_delete=(value = true)
-
@mark_for_delete = value
-
end
-
-
# Returns whether message will be marked for deletion.
-
# If so, the message will be deleted at session close (i.e. after #find
-
# exits), but only if also using the #find_and_delete method, or by
-
# calling #find with :delete_after_find set to true.
-
#
-
# Side-note: Just to be clear, this method will return true even if
-
# the message hasn't yet been marked for delete on the mail server.
-
# However, if this method returns true, it *will be* marked on the
-
# server after each block yields back to #find or #find_and_delete.
-
1
def is_marked_for_delete?
-
return @mark_for_delete
-
end
-
-
1
def text?
-
3
has_content_type? ? !!(main_type =~ /^text$/i) : false
-
end
-
-
1
private
-
-
# 2.1. General Description
-
# A message consists of header fields (collectively called "the header
-
# of the message") followed, optionally, by a body. The header is a
-
# sequence of lines of characters with special syntax as defined in
-
# this standard. The body is simply a sequence of characters that
-
# follows the header and is separated from the header by an empty line
-
# (i.e., a line with nothing preceding the CRLF).
-
#
-
# Additionally, I allow for the case where someone might have put whitespace
-
# on the "gap line"
-
1
def parse_message
-
167
header_part, body_part = raw_source.split(/#{CRLF}#{WSP}*#{CRLF}/m, 2)
-
# index = raw_source.index(/#{CRLF}#{WSP}*#{CRLF}/m, 2)
-
# self.header = (index) ? header_part[0,index] : nil
-
# lazy_body ( [raw_source, index+1])
-
167
self.header = header_part
-
167
self.body = body_part
-
end
-
-
1
def raw_source=(value)
-
236
@raw_source = value.to_crlf
-
end
-
-
# see comments to body=. We take data and process it lazily
-
1
def body_lazy(value)
-
323
process_body_raw if @body_raw && value
-
case
-
when value == nil || value.length<=0
-
192
@body = Mail::Body.new('')
-
192
@body_raw = nil
-
192
add_encoding_to_body
-
when @body && @body.multipart?
-
@body << Mail::Part.new(value)
-
add_encoding_to_body
-
else
-
131
@body_raw = value
-
# process_body_raw
-
323
end
-
end
-
-
-
1
def process_body_raw
-
130
@body = Mail::Body.new(@body_raw)
-
130
@body_raw = nil
-
130
separate_parts if @separate_parts
-
-
130
add_encoding_to_body
-
end
-
-
1
def set_envelope_header
-
171
if match_data = raw_source.to_s.match(/\AFrom\s(#{TEXT}+)#{CRLF}(.*)/m)
-
1
set_envelope(match_data[1])
-
1
self.raw_source = match_data[2]
-
end
-
end
-
-
1
def separate_parts
-
body.split!(boundary)
-
end
-
-
1
def add_encoding_to_body
-
322
if has_content_transfer_encoding?
-
15
@body.encoding = content_transfer_encoding
-
end
-
end
-
-
1
def identify_and_set_transfer_encoding
-
84
if body && body.multipart?
-
7
self.content_transfer_encoding = @transport_encoding
-
else
-
77
self.content_transfer_encoding = body.get_best_encoding(@transport_encoding)
-
end
-
end
-
-
1
def add_required_fields
-
84
add_multipart_mixed_header unless !body.multipart?
-
84
body = nil if body.nil?
-
84
add_message_id unless (has_message_id? || self.class == Mail::Part)
-
84
add_date unless has_date?
-
84
add_mime_version unless has_mime_version?
-
84
add_content_type unless has_content_type?
-
84
add_charset unless has_charset?
-
84
add_content_transfer_encoding unless has_content_transfer_encoding?
-
end
-
-
1
def add_multipart_alternate_header
-
header['content-type'] = ContentTypeField.with_boundary('multipart/alternative').value
-
header['content_type'].parameters[:charset] = @charset
-
body.boundary = boundary
-
end
-
-
1
def add_boundary
-
56
unless body.boundary && boundary
-
34
header['content-type'] = 'multipart/mixed' unless header['content-type']
-
34
header['content-type'].parameters[:boundary] = ContentTypeField.generate_boundary
-
34
header['content_type'].parameters[:charset] = @charset
-
34
body.boundary = boundary
-
end
-
end
-
-
1
def add_multipart_mixed_header
-
7
unless header['content-type']
-
header['content-type'] = ContentTypeField.with_boundary('multipart/mixed').value
-
header['content_type'].parameters[:charset] = @charset
-
body.boundary = boundary
-
end
-
end
-
-
1
def init_with_hash(hash)
-
64
passed_in_options = IndifferentHash.new(hash)
-
64
self.raw_source = ''
-
-
64
@header = Mail::Header.new
-
64
@body = Mail::Body.new
-
64
@body_raw = nil
-
-
# We need to store the body until last, as we need all headers added first
-
64
body_content = nil
-
-
64
passed_in_options.each_pair do |k,v|
-
205
k = underscoreize(k).to_sym if k.class == String
-
205
if k == :headers
-
self.headers(v)
-
elsif k == :body
-
64
body_content = v
-
else
-
141
self[k] = v
-
end
-
end
-
-
64
if body_content
-
64
self.body = body_content
-
64
if has_content_transfer_encoding?
-
13
body.encoding = content_transfer_encoding
-
end
-
end
-
end
-
-
1
def init_with_string(string)
-
171
self.raw_source = string
-
171
set_envelope_header
-
171
parse_message
-
171
@separate_parts = multipart?
-
end
-
-
# Returns the filename of the attachment (if it exists) or returns nil
-
1
def find_attachment
-
146
content_type_name = header[:content_type].filename rescue nil
-
146
content_disp_name = header[:content_disposition].filename rescue nil
-
146
content_loc_name = header[:content_location].location rescue nil
-
case
-
when content_type && content_type_name
-
44
filename = content_type_name
-
when content_disposition && content_disp_name
-
18
filename = content_disp_name
-
when content_location && content_loc_name
-
filename = content_loc_name
-
else
-
84
filename = nil
-
146
end
-
146
filename = Mail::Encodings.decode_encode(filename, :decode) if filename rescue filename
-
146
filename
-
end
-
-
1
def do_delivery
-
36
begin
-
36
if perform_deliveries
-
34
delivery_method.deliver!(self)
-
end
-
4
rescue Exception => e # Net::SMTP errors or sendmail pipe errors
-
4
raise e if raise_delivery_errors
-
end
-
end
-
-
1
def decode_body_as_text
-
body_text = decode_body
-
if charset
-
if RUBY_VERSION < '1.9'
-
require 'iconv'
-
return Iconv.conv("UTF-8//TRANSLIT//IGNORE", charset, body_text)
-
else
-
if encoding = Encoding.find(charset) rescue nil
-
body_text.force_encoding(encoding)
-
return body_text.encode(Encoding::UTF_8)
-
end
-
end
-
end
-
body_text
-
end
-
-
end
-
end
-
1
require 'mail/network/retriever_methods/base'
-
-
1
module Mail
-
1
autoload :SMTP, 'mail/network/delivery_methods/smtp'
-
1
autoload :FileDelivery, 'mail/network/delivery_methods/file_delivery'
-
1
autoload :Sendmail, 'mail/network/delivery_methods/sendmail'
-
1
autoload :Exim, 'mail/network/delivery_methods/exim'
-
1
autoload :SMTPConnection, 'mail/network/delivery_methods/smtp_connection'
-
1
autoload :TestMailer, 'mail/network/delivery_methods/test_mailer'
-
-
1
autoload :POP3, 'mail/network/retriever_methods/pop3'
-
1
autoload :IMAP, 'mail/network/retriever_methods/imap'
-
1
autoload :TestRetriever, 'mail/network/retriever_methods/test_retriever'
-
end
-
1
module Mail
-
-
# FileDelivery class delivers emails into multiple files based on the destination
-
# address. Each file is appended to if it already exists.
-
#
-
# So if you have an email going to fred@test, bob@test, joe@anothertest, and you
-
# set your location path to /path/to/mails then FileDelivery will create the directory
-
# if it does not exist, and put one copy of the email in three files, called
-
# by their message id
-
#
-
# Make sure the path you specify with :location is writable by the Ruby process
-
# running Mail.
-
1
class FileDelivery
-
-
1
if RUBY_VERSION >= '1.9.1'
-
1
require 'fileutils'
-
else
-
require 'ftools'
-
end
-
-
1
def initialize(values)
-
self.settings = { :location => './mails' }.merge!(values)
-
end
-
-
1
attr_accessor :settings
-
-
1
def deliver!(mail)
-
if ::File.respond_to?(:makedirs)
-
::File.makedirs settings[:location]
-
else
-
::FileUtils.mkdir_p settings[:location]
-
end
-
-
mail.destinations.uniq.each do |to|
-
::File.open(::File.join(settings[:location], File.basename(to.to_s)), 'a') { |f| "#{f.write(mail.encoded)}\r\n\r\n" }
-
end
-
end
-
-
end
-
end
-
1
module Mail
-
# A delivery method implementation which sends via sendmail.
-
#
-
# To use this, first find out where the sendmail binary is on your computer,
-
# if you are on a mac or unix box, it is usually in /usr/sbin/sendmail, this will
-
# be your sendmail location.
-
#
-
# Mail.defaults do
-
# delivery_method :sendmail
-
# end
-
#
-
# Or if your sendmail binary is not at '/usr/sbin/sendmail'
-
#
-
# Mail.defaults do
-
# delivery_method :sendmail, :location => '/absolute/path/to/your/sendmail'
-
# end
-
#
-
# Then just deliver the email as normal:
-
#
-
# Mail.deliver do
-
# to 'mikel@test.lindsaar.net'
-
# from 'ada@test.lindsaar.net'
-
# subject 'testing sendmail'
-
# body 'testing sendmail'
-
# end
-
#
-
# Or by calling deliver on a Mail message
-
#
-
# mail = Mail.new do
-
# to 'mikel@test.lindsaar.net'
-
# from 'ada@test.lindsaar.net'
-
# subject 'testing sendmail'
-
# body 'testing sendmail'
-
# end
-
#
-
# mail.deliver!
-
1
class Sendmail
-
-
1
def initialize(values)
-
self.settings = { :location => '/usr/sbin/sendmail',
-
:arguments => '-i -t' }.merge(values)
-
end
-
-
1
attr_accessor :settings
-
-
1
def deliver!(mail)
-
envelope_from = mail.return_path || mail.sender || mail.from_addrs.first
-
return_path = "-f " + '"' + envelope_from.escape_for_shell + '"' if envelope_from
-
-
arguments = [settings[:arguments], return_path].compact.join(" ")
-
-
self.class.call(settings[:location], arguments, mail.destinations.collect(&:escape_for_shell).join(" "), mail)
-
end
-
-
1
def self.call(path, arguments, destinations, mail)
-
IO.popen("#{path} #{arguments} #{destinations}", "w+") do |io|
-
io.puts mail.encoded.to_lf
-
io.flush
-
end
-
end
-
end
-
end
-
1
module Mail
-
# == Sending Email with SMTP
-
#
-
# Mail allows you to send emails using SMTP. This is done by wrapping Net::SMTP in
-
# an easy to use manner.
-
#
-
# === Sending via SMTP server on Localhost
-
#
-
# Sending locally (to a postfix or sendmail server running on localhost) requires
-
# no special setup. Just to Mail.deliver &block or message.deliver! and it will
-
# be sent in this method.
-
#
-
# === Sending via MobileMe
-
#
-
# Mail.defaults do
-
# delivery_method :smtp, { :address => "smtp.me.com",
-
# :port => 587,
-
# :domain => 'your.host.name',
-
# :user_name => '<username>',
-
# :password => '<password>',
-
# :authentication => 'plain',
-
# :enable_starttls_auto => true }
-
# end
-
#
-
# === Sending via GMail
-
#
-
# Mail.defaults do
-
# delivery_method :smtp, { :address => "smtp.gmail.com",
-
# :port => 587,
-
# :domain => 'your.host.name',
-
# :user_name => '<username>',
-
# :password => '<password>',
-
# :authentication => 'plain',
-
# :enable_starttls_auto => true }
-
# end
-
#
-
# === Certificate verification
-
#
-
# When using TLS, some mail servers provide certificates that are self-signed
-
# or whose names do not exactly match the hostname given in the address.
-
# OpenSSL will reject these by default. The best remedy is to use the correct
-
# hostname or update the certificate authorities trusted by your ruby. If
-
# that isn't possible, you can control this behavior with
-
# an :openssl_verify_mode setting. Its value may be either an OpenSSL
-
# verify mode constant (OpenSSL::SSL::VERIFY_NONE), or a string containing
-
# the name of an OpenSSL verify mode (none, peer, client_once,
-
# fail_if_no_peer_cert).
-
#
-
# === Others
-
#
-
# Feel free to send me other examples that were tricky
-
#
-
# === Delivering the email
-
#
-
# Once you have the settings right, sending the email is done by:
-
#
-
# Mail.deliver do
-
# to 'mikel@test.lindsaar.net'
-
# from 'ada@test.lindsaar.net'
-
# subject 'testing sendmail'
-
# body 'testing sendmail'
-
# end
-
#
-
# Or by calling deliver on a Mail message
-
#
-
# mail = Mail.new do
-
# to 'mikel@test.lindsaar.net'
-
# from 'ada@test.lindsaar.net'
-
# subject 'testing sendmail'
-
# body 'testing sendmail'
-
# end
-
#
-
# mail.deliver!
-
1
class SMTP
-
-
1
def initialize(values)
-
6
self.settings = { :address => "localhost",
-
:port => 25,
-
:domain => 'localhost.localdomain',
-
:user_name => nil,
-
:password => nil,
-
:authentication => nil,
-
:enable_starttls_auto => true,
-
:openssl_verify_mode => nil,
-
:ssl => nil,
-
:tls => nil
-
}.merge!(values)
-
end
-
-
1
attr_accessor :settings
-
-
# Send the message via SMTP.
-
# The from and to attributes are optional. If not set, they are retrieve from the Message.
-
1
def deliver!(mail)
-
-
# Set the envelope from to be either the return-path, the sender or the first from address
-
1
envelope_from = mail.return_path || mail.sender || mail.from_addrs.first
-
1
if envelope_from.blank?
-
raise ArgumentError.new('A sender (Return-Path, Sender or From) required to send a message')
-
end
-
-
1
destinations ||= mail.destinations if mail.respond_to?(:destinations) && mail.destinations
-
1
if destinations.blank?
-
raise ArgumentError.new('At least one recipient (To, Cc or Bcc) is required to send a message')
-
end
-
-
1
message ||= mail.encoded if mail.respond_to?(:encoded)
-
1
if message.blank?
-
raise ArgumentError.new('A encoded content is required to send a message')
-
end
-
-
1
smtp = Net::SMTP.new(settings[:address], settings[:port])
-
1
if settings[:tls] || settings[:ssl]
-
if smtp.respond_to?(:enable_tls)
-
unless settings[:openssl_verify_mode]
-
smtp.enable_tls
-
else
-
openssl_verify_mode = settings[:openssl_verify_mode]
-
if openssl_verify_mode.kind_of?(String)
-
openssl_verify_mode = "OpenSSL::SSL::VERIFY_#{openssl_verify_mode.upcase}".constantize
-
end
-
context = Net::SMTP.default_ssl_context
-
context.verify_mode = openssl_verify_mode
-
smtp.enable_tls(context)
-
end
-
end
-
elsif settings[:enable_starttls_auto]
-
1
if smtp.respond_to?(:enable_starttls_auto)
-
unless settings[:openssl_verify_mode]
-
smtp.enable_starttls_auto
-
else
-
openssl_verify_mode = settings[:openssl_verify_mode]
-
if openssl_verify_mode.kind_of?(String)
-
openssl_verify_mode = "OpenSSL::SSL::VERIFY_#{openssl_verify_mode.upcase}".constantize
-
end
-
context = Net::SMTP.default_ssl_context
-
context.verify_mode = openssl_verify_mode
-
smtp.enable_starttls_auto(context)
-
end
-
end
-
end
-
-
1
response = nil
-
1
smtp.start(settings[:domain], settings[:user_name], settings[:password], settings[:authentication]) do |smtp_obj|
-
1
response = smtp_obj.sendmail(message, envelope_from, destinations)
-
end
-
-
1
return settings[:return_response] ? response : self
-
end
-
-
-
end
-
end
-
1
module Mail
-
# The TestMailer is a bare bones mailer that does nothing. It is useful
-
# when you are testing.
-
#
-
# It also provides a template of the minimum methods you require to implement
-
# if you want to make a custom mailer for Mail
-
1
class TestMailer
-
-
# Provides a store of all the emails sent with the TestMailer so you can check them.
-
1
def TestMailer.deliveries
-
121
@@deliveries ||= []
-
end
-
-
# Allows you to over write the default deliveries store from an array to some
-
# other object. If you just want to clear the store,
-
# call TestMailer.deliveries.clear.
-
#
-
# If you place another object here, please make sure it responds to:
-
#
-
# * << (message)
-
# * clear
-
# * length
-
# * size
-
# * and other common Array methods
-
1
def TestMailer.deliveries=(val)
-
@@deliveries = val
-
end
-
-
1
def initialize(values)
-
107
@settings = {}
-
end
-
-
1
attr_accessor :settings
-
-
1
def deliver!(mail)
-
29
Mail::TestMailer.deliveries << mail
-
end
-
-
end
-
end
-
# encoding: utf-8
-
-
1
module Mail
-
-
1
class Retriever
-
-
# Get the oldest received email(s)
-
#
-
# Possible options:
-
# count: number of emails to retrieve. The default value is 1.
-
# order: order of emails returned. Possible values are :asc or :desc. Default value is :asc.
-
#
-
1
def first(options = {}, &block)
-
options ||= {}
-
options[:what] = :first
-
options[:count] ||= 1
-
find(options, &block)
-
end
-
-
# Get the most recent received email(s)
-
#
-
# Possible options:
-
# count: number of emails to retrieve. The default value is 1.
-
# order: order of emails returned. Possible values are :asc or :desc. Default value is :asc.
-
#
-
1
def last(options = {}, &block)
-
options ||= {}
-
options[:what] = :last
-
options[:count] ||= 1
-
find(options, &block)
-
end
-
-
# Get all emails.
-
#
-
# Possible options:
-
# order: order of emails returned. Possible values are :asc or :desc. Default value is :asc.
-
#
-
1
def all(options = {}, &block)
-
options ||= {}
-
options[:count] = :all
-
find(options, &block)
-
end
-
-
# Find emails in the mailbox, and then deletes them. Without any options, the
-
# five last received emails are returned.
-
#
-
# Possible options:
-
# what: last or first emails. The default is :first.
-
# order: order of emails returned. Possible values are :asc or :desc. Default value is :asc.
-
# count: number of emails to retrieve. The default value is 10. A value of 1 returns an
-
# instance of Message, not an array of Message instances.
-
# delete_after_find: flag for whether to delete each retreived email after find. Default
-
# is true. Call #find if you would like this to default to false.
-
#
-
1
def find_and_delete(options = {}, &block)
-
options ||= {}
-
options[:delete_after_find] ||= true
-
find(options, &block)
-
end
-
-
end
-
-
end
-
# Autogenerated from a Treetop grammar. Edits may be lost.
-
-
-
1
module Mail
-
1
module AddressLists
-
1
include Treetop::Runtime
-
-
1
def root
-
289
@root ||= :primary_address
-
end
-
-
1
include RFC2822
-
-
1
module PrimaryAddress0
-
1
def addresses
-
578
([first_addr] + other_addr.elements.map { |o| o.addr_value }).reject { |e| e.empty? }
-
end
-
end
-
-
1
module PrimaryAddress1
-
1
def addresses
-
[first_addr] + other_addr.elements.map { |o| o.addr_value }
-
end
-
end
-
-
1
def _nt_primary_address
-
289
start_index = index
-
289
if node_cache[:primary_address].has_key?(index)
-
cached = node_cache[:primary_address][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
289
i0 = index
-
289
r1 = _nt_address_list
-
289
r1.extend(PrimaryAddress0)
-
289
if r1
-
289
r0 = r1
-
else
-
r2 = _nt_obs_addr_list
-
r2.extend(PrimaryAddress1)
-
if r2
-
r0 = r2
-
else
-
@index = i0
-
r0 = nil
-
end
-
end
-
-
289
node_cache[:primary_address][start_index] = r0
-
-
289
r0
-
end
-
-
end
-
-
1
class AddressListsParser < Treetop::Runtime::CompiledParser
-
1
include AddressLists
-
end
-
-
end
-
# Autogenerated from a Treetop grammar. Edits may be lost.
-
-
-
1
module Mail
-
1
module ContentDisposition
-
1
include Treetop::Runtime
-
-
1
def root
-
12
@root ||= :content_disposition
-
end
-
-
1
include RFC2822
-
-
1
include RFC2045
-
-
1
module ContentDisposition0
-
1
def CFWS1
-
elements[0]
-
end
-
-
1
def parameter
-
12
elements[2]
-
end
-
-
1
def CFWS2
-
elements[3]
-
end
-
end
-
-
1
module ContentDisposition1
-
1
def disposition_type
-
12
elements[0]
-
end
-
-
1
def param_hashes
-
12
elements[1]
-
end
-
end
-
-
1
module ContentDisposition2
-
1
def parameters
-
12
param_hashes.elements.map do |param|
-
12
param.parameter.param_hash
-
end
-
end
-
end
-
-
1
def _nt_content_disposition
-
12
start_index = index
-
12
if node_cache[:content_disposition].has_key?(index)
-
cached = node_cache[:content_disposition][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
12
i0, s0 = index, []
-
12
r1 = _nt_disposition_type
-
12
s0 << r1
-
12
if r1
-
12
s2, i2 = [], index
-
12
loop do
-
24
i3, s3 = index, []
-
24
r4 = _nt_CFWS
-
24
s3 << r4
-
24
if r4
-
24
if has_terminal?(";", false, index)
-
12
r5 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
12
@index += 1
-
else
-
12
terminal_parse_failure(";")
-
12
r5 = nil
-
end
-
24
s3 << r5
-
24
if r5
-
12
r6 = _nt_parameter
-
12
s3 << r6
-
12
if r6
-
12
r7 = _nt_CFWS
-
12
s3 << r7
-
end
-
end
-
end
-
24
if s3.last
-
12
r3 = instantiate_node(SyntaxNode,input, i3...index, s3)
-
12
r3.extend(ContentDisposition0)
-
else
-
12
@index = i3
-
12
r3 = nil
-
end
-
24
if r3
-
12
s2 << r3
-
else
-
12
break
-
end
-
end
-
12
r2 = instantiate_node(SyntaxNode,input, i2...index, s2)
-
12
s0 << r2
-
end
-
12
if s0.last
-
12
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
12
r0.extend(ContentDisposition1)
-
12
r0.extend(ContentDisposition2)
-
else
-
@index = i0
-
r0 = nil
-
end
-
-
12
node_cache[:content_disposition][start_index] = r0
-
-
12
r0
-
end
-
-
1
module DispositionType0
-
end
-
-
1
module DispositionType1
-
end
-
-
1
def _nt_disposition_type
-
12
start_index = index
-
12
if node_cache[:disposition_type].has_key?(index)
-
cached = node_cache[:disposition_type][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
12
i0 = index
-
12
i1, s1 = index, []
-
12
if has_terminal?('\G[iI]', true, index)
-
2
r2 = true
-
2
@index += 1
-
else
-
10
r2 = nil
-
end
-
12
s1 << r2
-
12
if r2
-
2
if has_terminal?('\G[nN]', true, index)
-
2
r3 = true
-
2
@index += 1
-
else
-
r3 = nil
-
end
-
2
s1 << r3
-
2
if r3
-
2
if has_terminal?('\G[lL]', true, index)
-
2
r4 = true
-
2
@index += 1
-
else
-
r4 = nil
-
end
-
2
s1 << r4
-
2
if r4
-
2
if has_terminal?('\G[iI]', true, index)
-
2
r5 = true
-
2
@index += 1
-
else
-
r5 = nil
-
end
-
2
s1 << r5
-
2
if r5
-
2
if has_terminal?('\G[nN]', true, index)
-
2
r6 = true
-
2
@index += 1
-
else
-
r6 = nil
-
end
-
2
s1 << r6
-
2
if r6
-
2
if has_terminal?('\G[eE]', true, index)
-
2
r7 = true
-
2
@index += 1
-
else
-
r7 = nil
-
end
-
2
s1 << r7
-
end
-
end
-
end
-
end
-
end
-
12
if s1.last
-
2
r1 = instantiate_node(SyntaxNode,input, i1...index, s1)
-
2
r1.extend(DispositionType0)
-
else
-
10
@index = i1
-
10
r1 = nil
-
end
-
12
if r1
-
2
r0 = r1
-
else
-
10
i8, s8 = index, []
-
10
if has_terminal?('\G[aA]', true, index)
-
10
r9 = true
-
10
@index += 1
-
else
-
r9 = nil
-
end
-
10
s8 << r9
-
10
if r9
-
10
if has_terminal?('\G[tT]', true, index)
-
10
r10 = true
-
10
@index += 1
-
else
-
r10 = nil
-
end
-
10
s8 << r10
-
10
if r10
-
10
if has_terminal?('\G[tT]', true, index)
-
10
r11 = true
-
10
@index += 1
-
else
-
r11 = nil
-
end
-
10
s8 << r11
-
10
if r11
-
10
if has_terminal?('\G[aA]', true, index)
-
10
r12 = true
-
10
@index += 1
-
else
-
r12 = nil
-
end
-
10
s8 << r12
-
10
if r12
-
10
if has_terminal?('\G[cC]', true, index)
-
10
r13 = true
-
10
@index += 1
-
else
-
r13 = nil
-
end
-
10
s8 << r13
-
10
if r13
-
10
if has_terminal?('\G[hH]', true, index)
-
10
r14 = true
-
10
@index += 1
-
else
-
r14 = nil
-
end
-
10
s8 << r14
-
10
if r14
-
10
if has_terminal?('\G[mM]', true, index)
-
10
r15 = true
-
10
@index += 1
-
else
-
r15 = nil
-
end
-
10
s8 << r15
-
10
if r15
-
10
if has_terminal?('\G[eE]', true, index)
-
10
r16 = true
-
10
@index += 1
-
else
-
r16 = nil
-
end
-
10
s8 << r16
-
10
if r16
-
10
if has_terminal?('\G[nN]', true, index)
-
10
r17 = true
-
10
@index += 1
-
else
-
r17 = nil
-
end
-
10
s8 << r17
-
10
if r17
-
10
if has_terminal?('\G[tT]', true, index)
-
10
r18 = true
-
10
@index += 1
-
else
-
r18 = nil
-
end
-
10
s8 << r18
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
10
if s8.last
-
10
r8 = instantiate_node(SyntaxNode,input, i8...index, s8)
-
10
r8.extend(DispositionType1)
-
else
-
@index = i8
-
r8 = nil
-
end
-
10
if r8
-
10
r0 = r8
-
else
-
r19 = _nt_extension_token
-
if r19
-
r0 = r19
-
else
-
if has_terminal?('', false, index)
-
r20 = instantiate_node(SyntaxNode,input, index...(index + 0))
-
@index += 0
-
else
-
terminal_parse_failure('')
-
r20 = nil
-
end
-
if r20
-
r0 = r20
-
else
-
@index = i0
-
r0 = nil
-
end
-
end
-
end
-
end
-
-
12
node_cache[:disposition_type][start_index] = r0
-
-
12
r0
-
end
-
-
1
def _nt_extension_token
-
start_index = index
-
if node_cache[:extension_token].has_key?(index)
-
cached = node_cache[:extension_token][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
i0 = index
-
r1 = _nt_ietf_token
-
if r1
-
r0 = r1
-
else
-
r2 = _nt_custom_x_token
-
if r2
-
r0 = r2
-
else
-
@index = i0
-
r0 = nil
-
end
-
end
-
-
node_cache[:extension_token][start_index] = r0
-
-
r0
-
end
-
-
1
module Parameter0
-
1
def attr
-
12
elements[1]
-
end
-
-
1
def val
-
12
elements[3]
-
end
-
-
end
-
-
1
module Parameter1
-
1
def param_hash
-
12
{attr.text_value => val.text_value}
-
end
-
end
-
-
1
def _nt_parameter
-
12
start_index = index
-
12
if node_cache[:parameter].has_key?(index)
-
cached = node_cache[:parameter][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
12
i0, s0 = index, []
-
12
r2 = _nt_CFWS
-
12
if r2
-
12
r1 = r2
-
else
-
r1 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
12
s0 << r1
-
12
if r1
-
12
r3 = _nt_attribute
-
12
s0 << r3
-
12
if r3
-
12
if has_terminal?("=", false, index)
-
12
r4 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
12
@index += 1
-
else
-
terminal_parse_failure("=")
-
r4 = nil
-
end
-
12
s0 << r4
-
12
if r4
-
12
r5 = _nt_value
-
12
s0 << r5
-
12
if r5
-
12
r7 = _nt_CFWS
-
12
if r7
-
12
r6 = r7
-
else
-
r6 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
12
s0 << r6
-
end
-
end
-
end
-
end
-
12
if s0.last
-
12
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
12
r0.extend(Parameter0)
-
12
r0.extend(Parameter1)
-
else
-
@index = i0
-
r0 = nil
-
end
-
-
12
node_cache[:parameter][start_index] = r0
-
-
12
r0
-
end
-
-
1
def _nt_attribute
-
12
start_index = index
-
12
if node_cache[:attribute].has_key?(index)
-
cached = node_cache[:attribute][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
12
s0, i0 = [], index
-
12
loop do
-
108
r1 = _nt_token
-
108
if r1
-
96
s0 << r1
-
else
-
12
break
-
end
-
end
-
12
if s0.empty?
-
@index = i0
-
r0 = nil
-
else
-
12
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
end
-
-
12
node_cache[:attribute][start_index] = r0
-
-
12
r0
-
end
-
-
1
module Value0
-
1
def text_value
-
12
quoted_content.text_value
-
end
-
end
-
-
1
def _nt_value
-
12
start_index = index
-
12
if node_cache[:value].has_key?(index)
-
cached = node_cache[:value][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
12
i0 = index
-
12
r1 = _nt_quoted_string
-
12
r1.extend(Value0)
-
12
if r1
-
12
r0 = r1
-
else
-
s2, i2 = [], index
-
loop do
-
i3 = index
-
r4 = _nt_token
-
if r4
-
r3 = r4
-
else
-
if has_terminal?('\G[\\x3d]', true, index)
-
r5 = true
-
@index += 1
-
else
-
r5 = nil
-
end
-
if r5
-
r3 = r5
-
else
-
@index = i3
-
r3 = nil
-
end
-
end
-
if r3
-
s2 << r3
-
else
-
break
-
end
-
end
-
if s2.empty?
-
@index = i2
-
r2 = nil
-
else
-
r2 = instantiate_node(SyntaxNode,input, i2...index, s2)
-
end
-
if r2
-
r0 = r2
-
else
-
@index = i0
-
r0 = nil
-
end
-
end
-
-
12
node_cache[:value][start_index] = r0
-
-
12
r0
-
end
-
-
end
-
-
1
class ContentDispositionParser < Treetop::Runtime::CompiledParser
-
1
include ContentDisposition
-
end
-
-
end
-
# Autogenerated from a Treetop grammar. Edits may be lost.
-
-
-
1
module Mail
-
1
module ContentLocation
-
1
include Treetop::Runtime
-
-
1
def root
-
@root ||= :primary
-
end
-
-
1
include RFC2822
-
-
1
include RFC2045
-
-
1
module Primary0
-
1
def CFWS1
-
elements[0]
-
end
-
-
1
def location
-
elements[1]
-
end
-
-
1
def CFWS2
-
elements[2]
-
end
-
end
-
-
1
def _nt_primary
-
start_index = index
-
if node_cache[:primary].has_key?(index)
-
cached = node_cache[:primary][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
i0, s0 = index, []
-
r1 = _nt_CFWS
-
s0 << r1
-
if r1
-
r2 = _nt_location
-
s0 << r2
-
if r2
-
r3 = _nt_CFWS
-
s0 << r3
-
end
-
end
-
if s0.last
-
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
r0.extend(Primary0)
-
else
-
@index = i0
-
r0 = nil
-
end
-
-
node_cache[:primary][start_index] = r0
-
-
r0
-
end
-
-
1
module Location0
-
1
def text_value
-
quoted_content.text_value
-
end
-
end
-
-
1
def _nt_location
-
start_index = index
-
if node_cache[:location].has_key?(index)
-
cached = node_cache[:location][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
i0 = index
-
r1 = _nt_quoted_string
-
r1.extend(Location0)
-
if r1
-
r0 = r1
-
else
-
s2, i2 = [], index
-
loop do
-
i3 = index
-
r4 = _nt_token
-
if r4
-
r3 = r4
-
else
-
if has_terminal?('\G[\\x3d]', true, index)
-
r5 = true
-
@index += 1
-
else
-
r5 = nil
-
end
-
if r5
-
r3 = r5
-
else
-
@index = i3
-
r3 = nil
-
end
-
end
-
if r3
-
s2 << r3
-
else
-
break
-
end
-
end
-
if s2.empty?
-
@index = i2
-
r2 = nil
-
else
-
r2 = instantiate_node(SyntaxNode,input, i2...index, s2)
-
end
-
if r2
-
r0 = r2
-
else
-
@index = i0
-
r0 = nil
-
end
-
end
-
-
node_cache[:location][start_index] = r0
-
-
r0
-
end
-
-
end
-
-
1
class ContentLocationParser < Treetop::Runtime::CompiledParser
-
1
include ContentLocation
-
end
-
-
end
-
# Autogenerated from a Treetop grammar. Edits may be lost.
-
-
-
1
module Mail
-
1
module ContentTransferEncoding
-
1
include Treetop::Runtime
-
-
1
def root
-
99
@root ||= :primary
-
end
-
-
1
include RFC2822
-
-
1
include RFC2045
-
-
1
module Primary0
-
1
def CFWS1
-
elements[0]
-
end
-
-
1
def encoding
-
99
elements[1]
-
end
-
-
1
def CFWS2
-
elements[2]
-
end
-
-
1
def CFWS3
-
elements[4]
-
end
-
end
-
-
1
def _nt_primary
-
99
start_index = index
-
99
if node_cache[:primary].has_key?(index)
-
cached = node_cache[:primary][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
99
i0, s0 = index, []
-
99
r1 = _nt_CFWS
-
99
s0 << r1
-
99
if r1
-
99
r2 = _nt_encoding
-
99
s0 << r2
-
99
if r2
-
99
r3 = _nt_CFWS
-
99
s0 << r3
-
99
if r3
-
99
if has_terminal?(";", false, index)
-
r5 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
99
terminal_parse_failure(";")
-
99
r5 = nil
-
end
-
99
if r5
-
r4 = r5
-
else
-
99
r4 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
99
s0 << r4
-
99
if r4
-
99
r6 = _nt_CFWS
-
99
s0 << r6
-
end
-
end
-
end
-
end
-
99
if s0.last
-
99
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
99
r0.extend(Primary0)
-
else
-
@index = i0
-
r0 = nil
-
end
-
-
99
node_cache[:primary][start_index] = r0
-
-
99
r0
-
end
-
-
1
module Encoding0
-
1
def ietf_token
-
99
elements[0]
-
end
-
-
end
-
-
1
module Encoding1
-
1
def text_value
-
99
ietf_token.text_value
-
end
-
end
-
-
1
def _nt_encoding
-
99
start_index = index
-
99
if node_cache[:encoding].has_key?(index)
-
cached = node_cache[:encoding][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
99
i0 = index
-
99
i1, s1 = index, []
-
99
r2 = _nt_ietf_token
-
99
s1 << r2
-
99
if r2
-
99
if has_terminal?("s", false, index)
-
r4 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
99
terminal_parse_failure("s")
-
99
r4 = nil
-
end
-
99
if r4
-
r3 = r4
-
else
-
99
r3 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
99
s1 << r3
-
end
-
99
if s1.last
-
99
r1 = instantiate_node(SyntaxNode,input, i1...index, s1)
-
99
r1.extend(Encoding0)
-
99
r1.extend(Encoding1)
-
else
-
@index = i1
-
r1 = nil
-
end
-
99
if r1
-
99
r0 = r1
-
else
-
r5 = _nt_custom_x_token
-
if r5
-
r0 = r5
-
else
-
@index = i0
-
r0 = nil
-
end
-
end
-
-
99
node_cache[:encoding][start_index] = r0
-
-
99
r0
-
end
-
-
end
-
-
1
class ContentTransferEncodingParser < Treetop::Runtime::CompiledParser
-
1
include ContentTransferEncoding
-
end
-
-
end
-
# Autogenerated from a Treetop grammar. Edits may be lost.
-
-
-
1
module Mail
-
1
module ContentType
-
1
include Treetop::Runtime
-
-
1
def root
-
352
@root ||= :content_type
-
end
-
-
1
include RFC2822
-
-
1
include RFC2045
-
-
1
module ContentType0
-
1
def CFWS1
-
elements[0]
-
end
-
-
1
def parameter
-
114
elements[2]
-
end
-
-
1
def CFWS2
-
elements[3]
-
end
-
end
-
-
1
module ContentType1
-
1
def main_type
-
352
elements[0]
-
end
-
-
1
def sub_type
-
352
elements[2]
-
end
-
-
1
def param_hashes
-
352
elements[3]
-
end
-
end
-
-
1
module ContentType2
-
1
def parameters
-
352
param_hashes.elements.map do |param|
-
114
param.parameter.param_hash
-
end
-
end
-
end
-
-
1
def _nt_content_type
-
352
start_index = index
-
352
if node_cache[:content_type].has_key?(index)
-
cached = node_cache[:content_type][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
352
i0, s0 = index, []
-
352
r1 = _nt_main_type
-
352
s0 << r1
-
352
if r1
-
352
if has_terminal?("/", false, index)
-
352
r2 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
352
@index += 1
-
else
-
terminal_parse_failure("/")
-
r2 = nil
-
end
-
352
s0 << r2
-
352
if r2
-
352
r3 = _nt_sub_type
-
352
s0 << r3
-
352
if r3
-
352
s4, i4 = [], index
-
352
loop do
-
466
i5, s5 = index, []
-
466
r6 = _nt_CFWS
-
466
s5 << r6
-
466
if r6
-
466
if has_terminal?(";", false, index)
-
114
r8 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
114
@index += 1
-
else
-
352
terminal_parse_failure(";")
-
352
r8 = nil
-
end
-
466
if r8
-
114
r7 = r8
-
else
-
352
r7 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
466
s5 << r7
-
466
if r7
-
466
r9 = _nt_parameter
-
466
s5 << r9
-
466
if r9
-
114
r10 = _nt_CFWS
-
114
s5 << r10
-
end
-
end
-
end
-
466
if s5.last
-
114
r5 = instantiate_node(SyntaxNode,input, i5...index, s5)
-
114
r5.extend(ContentType0)
-
else
-
352
@index = i5
-
352
r5 = nil
-
end
-
466
if r5
-
114
s4 << r5
-
else
-
352
break
-
end
-
end
-
352
r4 = instantiate_node(SyntaxNode,input, i4...index, s4)
-
352
s0 << r4
-
end
-
end
-
end
-
352
if s0.last
-
352
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
352
r0.extend(ContentType1)
-
352
r0.extend(ContentType2)
-
else
-
@index = i0
-
r0 = nil
-
end
-
-
352
node_cache[:content_type][start_index] = r0
-
-
352
r0
-
end
-
-
1
def _nt_main_type
-
352
start_index = index
-
352
if node_cache[:main_type].has_key?(index)
-
cached = node_cache[:main_type][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
352
i0 = index
-
352
r1 = _nt_discrete_type
-
352
if r1
-
287
r0 = r1
-
else
-
65
r2 = _nt_composite_type
-
65
if r2
-
65
r0 = r2
-
else
-
@index = i0
-
r0 = nil
-
end
-
end
-
-
352
node_cache[:main_type][start_index] = r0
-
-
352
r0
-
end
-
-
1
module DiscreteType0
-
end
-
-
1
module DiscreteType1
-
end
-
-
1
module DiscreteType2
-
end
-
-
1
module DiscreteType3
-
end
-
-
1
module DiscreteType4
-
end
-
-
1
def _nt_discrete_type
-
352
start_index = index
-
352
if node_cache[:discrete_type].has_key?(index)
-
cached = node_cache[:discrete_type][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
352
i0 = index
-
352
i1, s1 = index, []
-
352
if has_terminal?('\G[tT]', true, index)
-
275
r2 = true
-
275
@index += 1
-
else
-
77
r2 = nil
-
end
-
352
s1 << r2
-
352
if r2
-
275
if has_terminal?('\G[eE]', true, index)
-
275
r3 = true
-
275
@index += 1
-
else
-
r3 = nil
-
end
-
275
s1 << r3
-
275
if r3
-
275
if has_terminal?('\G[xX]', true, index)
-
275
r4 = true
-
275
@index += 1
-
else
-
r4 = nil
-
end
-
275
s1 << r4
-
275
if r4
-
275
if has_terminal?('\G[tT]', true, index)
-
275
r5 = true
-
275
@index += 1
-
else
-
r5 = nil
-
end
-
275
s1 << r5
-
end
-
end
-
end
-
352
if s1.last
-
275
r1 = instantiate_node(SyntaxNode,input, i1...index, s1)
-
275
r1.extend(DiscreteType0)
-
else
-
77
@index = i1
-
77
r1 = nil
-
end
-
352
if r1
-
275
r0 = r1
-
else
-
77
i6, s6 = index, []
-
77
if has_terminal?('\G[iI]', true, index)
-
4
r7 = true
-
4
@index += 1
-
else
-
73
r7 = nil
-
end
-
77
s6 << r7
-
77
if r7
-
4
if has_terminal?('\G[mM]', true, index)
-
4
r8 = true
-
4
@index += 1
-
else
-
r8 = nil
-
end
-
4
s6 << r8
-
4
if r8
-
4
if has_terminal?('\G[aA]', true, index)
-
4
r9 = true
-
4
@index += 1
-
else
-
r9 = nil
-
end
-
4
s6 << r9
-
4
if r9
-
4
if has_terminal?('\G[gG]', true, index)
-
4
r10 = true
-
4
@index += 1
-
else
-
r10 = nil
-
end
-
4
s6 << r10
-
4
if r10
-
4
if has_terminal?('\G[eE]', true, index)
-
4
r11 = true
-
4
@index += 1
-
else
-
r11 = nil
-
end
-
4
s6 << r11
-
end
-
end
-
end
-
end
-
77
if s6.last
-
4
r6 = instantiate_node(SyntaxNode,input, i6...index, s6)
-
4
r6.extend(DiscreteType1)
-
else
-
73
@index = i6
-
73
r6 = nil
-
end
-
77
if r6
-
4
r0 = r6
-
else
-
73
i12, s12 = index, []
-
73
if has_terminal?('\G[aA]', true, index)
-
8
r13 = true
-
8
@index += 1
-
else
-
65
r13 = nil
-
end
-
73
s12 << r13
-
73
if r13
-
8
if has_terminal?('\G[uU]', true, index)
-
r14 = true
-
@index += 1
-
else
-
8
r14 = nil
-
end
-
8
s12 << r14
-
8
if r14
-
if has_terminal?('\G[dD]', true, index)
-
r15 = true
-
@index += 1
-
else
-
r15 = nil
-
end
-
s12 << r15
-
if r15
-
if has_terminal?('\G[iI]', true, index)
-
r16 = true
-
@index += 1
-
else
-
r16 = nil
-
end
-
s12 << r16
-
if r16
-
if has_terminal?('\G[oO]', true, index)
-
r17 = true
-
@index += 1
-
else
-
r17 = nil
-
end
-
s12 << r17
-
end
-
end
-
end
-
end
-
73
if s12.last
-
r12 = instantiate_node(SyntaxNode,input, i12...index, s12)
-
r12.extend(DiscreteType2)
-
else
-
73
@index = i12
-
73
r12 = nil
-
end
-
73
if r12
-
r0 = r12
-
else
-
73
i18, s18 = index, []
-
73
if has_terminal?('\G[vV]', true, index)
-
r19 = true
-
@index += 1
-
else
-
73
r19 = nil
-
end
-
73
s18 << r19
-
73
if r19
-
if has_terminal?('\G[iI]', true, index)
-
r20 = true
-
@index += 1
-
else
-
r20 = nil
-
end
-
s18 << r20
-
if r20
-
if has_terminal?('\G[dD]', true, index)
-
r21 = true
-
@index += 1
-
else
-
r21 = nil
-
end
-
s18 << r21
-
if r21
-
if has_terminal?('\G[eE]', true, index)
-
r22 = true
-
@index += 1
-
else
-
r22 = nil
-
end
-
s18 << r22
-
if r22
-
if has_terminal?('\G[oO]', true, index)
-
r23 = true
-
@index += 1
-
else
-
r23 = nil
-
end
-
s18 << r23
-
end
-
end
-
end
-
end
-
73
if s18.last
-
r18 = instantiate_node(SyntaxNode,input, i18...index, s18)
-
r18.extend(DiscreteType3)
-
else
-
73
@index = i18
-
73
r18 = nil
-
end
-
73
if r18
-
r0 = r18
-
else
-
73
i24, s24 = index, []
-
73
if has_terminal?('\G[aA]', true, index)
-
8
r25 = true
-
8
@index += 1
-
else
-
65
r25 = nil
-
end
-
73
s24 << r25
-
73
if r25
-
8
if has_terminal?('\G[pP]', true, index)
-
8
r26 = true
-
8
@index += 1
-
else
-
r26 = nil
-
end
-
8
s24 << r26
-
8
if r26
-
8
if has_terminal?('\G[pP]', true, index)
-
8
r27 = true
-
8
@index += 1
-
else
-
r27 = nil
-
end
-
8
s24 << r27
-
8
if r27
-
8
if has_terminal?('\G[lL]', true, index)
-
8
r28 = true
-
8
@index += 1
-
else
-
r28 = nil
-
end
-
8
s24 << r28
-
8
if r28
-
8
if has_terminal?('\G[iI]', true, index)
-
8
r29 = true
-
8
@index += 1
-
else
-
r29 = nil
-
end
-
8
s24 << r29
-
8
if r29
-
8
if has_terminal?('\G[cC]', true, index)
-
8
r30 = true
-
8
@index += 1
-
else
-
r30 = nil
-
end
-
8
s24 << r30
-
8
if r30
-
8
if has_terminal?('\G[aA]', true, index)
-
8
r31 = true
-
8
@index += 1
-
else
-
r31 = nil
-
end
-
8
s24 << r31
-
8
if r31
-
8
if has_terminal?('\G[tT]', true, index)
-
8
r32 = true
-
8
@index += 1
-
else
-
r32 = nil
-
end
-
8
s24 << r32
-
8
if r32
-
8
if has_terminal?('\G[iI]', true, index)
-
8
r33 = true
-
8
@index += 1
-
else
-
r33 = nil
-
end
-
8
s24 << r33
-
8
if r33
-
8
if has_terminal?('\G[oO]', true, index)
-
8
r34 = true
-
8
@index += 1
-
else
-
r34 = nil
-
end
-
8
s24 << r34
-
8
if r34
-
8
if has_terminal?('\G[nN]', true, index)
-
8
r35 = true
-
8
@index += 1
-
else
-
r35 = nil
-
end
-
8
s24 << r35
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
73
if s24.last
-
8
r24 = instantiate_node(SyntaxNode,input, i24...index, s24)
-
8
r24.extend(DiscreteType4)
-
else
-
65
@index = i24
-
65
r24 = nil
-
end
-
73
if r24
-
8
r0 = r24
-
else
-
65
r36 = _nt_extension_token
-
65
if r36
-
r0 = r36
-
else
-
65
@index = i0
-
65
r0 = nil
-
end
-
end
-
end
-
end
-
end
-
end
-
-
352
node_cache[:discrete_type][start_index] = r0
-
-
352
r0
-
end
-
-
1
module CompositeType0
-
end
-
-
1
module CompositeType1
-
end
-
-
1
def _nt_composite_type
-
65
start_index = index
-
65
if node_cache[:composite_type].has_key?(index)
-
cached = node_cache[:composite_type][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
65
i0 = index
-
65
i1, s1 = index, []
-
65
if has_terminal?('\G[mM]', true, index)
-
65
r2 = true
-
65
@index += 1
-
else
-
r2 = nil
-
end
-
65
s1 << r2
-
65
if r2
-
65
if has_terminal?('\G[eE]', true, index)
-
r3 = true
-
@index += 1
-
else
-
65
r3 = nil
-
end
-
65
s1 << r3
-
65
if r3
-
if has_terminal?('\G[sS]', true, index)
-
r4 = true
-
@index += 1
-
else
-
r4 = nil
-
end
-
s1 << r4
-
if r4
-
if has_terminal?('\G[sS]', true, index)
-
r5 = true
-
@index += 1
-
else
-
r5 = nil
-
end
-
s1 << r5
-
if r5
-
if has_terminal?('\G[aA]', true, index)
-
r6 = true
-
@index += 1
-
else
-
r6 = nil
-
end
-
s1 << r6
-
if r6
-
if has_terminal?('\G[gG]', true, index)
-
r7 = true
-
@index += 1
-
else
-
r7 = nil
-
end
-
s1 << r7
-
if r7
-
if has_terminal?('\G[eE]', true, index)
-
r8 = true
-
@index += 1
-
else
-
r8 = nil
-
end
-
s1 << r8
-
end
-
end
-
end
-
end
-
end
-
end
-
65
if s1.last
-
r1 = instantiate_node(SyntaxNode,input, i1...index, s1)
-
r1.extend(CompositeType0)
-
else
-
65
@index = i1
-
65
r1 = nil
-
end
-
65
if r1
-
r0 = r1
-
else
-
65
i9, s9 = index, []
-
65
if has_terminal?('\G[mM]', true, index)
-
65
r10 = true
-
65
@index += 1
-
else
-
r10 = nil
-
end
-
65
s9 << r10
-
65
if r10
-
65
if has_terminal?('\G[uU]', true, index)
-
65
r11 = true
-
65
@index += 1
-
else
-
r11 = nil
-
end
-
65
s9 << r11
-
65
if r11
-
65
if has_terminal?('\G[lL]', true, index)
-
65
r12 = true
-
65
@index += 1
-
else
-
r12 = nil
-
end
-
65
s9 << r12
-
65
if r12
-
65
if has_terminal?('\G[tT]', true, index)
-
65
r13 = true
-
65
@index += 1
-
else
-
r13 = nil
-
end
-
65
s9 << r13
-
65
if r13
-
65
if has_terminal?('\G[iI]', true, index)
-
65
r14 = true
-
65
@index += 1
-
else
-
r14 = nil
-
end
-
65
s9 << r14
-
65
if r14
-
65
if has_terminal?('\G[pP]', true, index)
-
65
r15 = true
-
65
@index += 1
-
else
-
r15 = nil
-
end
-
65
s9 << r15
-
65
if r15
-
65
if has_terminal?('\G[aA]', true, index)
-
65
r16 = true
-
65
@index += 1
-
else
-
r16 = nil
-
end
-
65
s9 << r16
-
65
if r16
-
65
if has_terminal?('\G[rR]', true, index)
-
65
r17 = true
-
65
@index += 1
-
else
-
r17 = nil
-
end
-
65
s9 << r17
-
65
if r17
-
65
if has_terminal?('\G[tT]', true, index)
-
65
r18 = true
-
65
@index += 1
-
else
-
r18 = nil
-
end
-
65
s9 << r18
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
65
if s9.last
-
65
r9 = instantiate_node(SyntaxNode,input, i9...index, s9)
-
65
r9.extend(CompositeType1)
-
else
-
@index = i9
-
r9 = nil
-
end
-
65
if r9
-
65
r0 = r9
-
else
-
r19 = _nt_extension_token
-
if r19
-
r0 = r19
-
else
-
@index = i0
-
r0 = nil
-
end
-
end
-
end
-
-
65
node_cache[:composite_type][start_index] = r0
-
-
65
r0
-
end
-
-
1
def _nt_extension_token
-
417
start_index = index
-
417
if node_cache[:extension_token].has_key?(index)
-
cached = node_cache[:extension_token][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
417
i0 = index
-
417
r1 = _nt_ietf_token
-
417
if r1
-
r0 = r1
-
else
-
417
r2 = _nt_custom_x_token
-
417
if r2
-
2
r0 = r2
-
else
-
415
@index = i0
-
415
r0 = nil
-
end
-
end
-
-
417
node_cache[:extension_token][start_index] = r0
-
-
417
r0
-
end
-
-
1
def _nt_sub_type
-
352
start_index = index
-
352
if node_cache[:sub_type].has_key?(index)
-
cached = node_cache[:sub_type][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
352
i0 = index
-
352
r1 = _nt_extension_token
-
352
if r1
-
2
r0 = r1
-
else
-
350
r2 = _nt_iana_token
-
350
if r2
-
350
r0 = r2
-
else
-
@index = i0
-
r0 = nil
-
end
-
end
-
-
352
node_cache[:sub_type][start_index] = r0
-
-
352
r0
-
end
-
-
1
module Parameter0
-
1
def attr
-
114
elements[1]
-
end
-
-
1
def val
-
114
elements[3]
-
end
-
-
end
-
-
1
module Parameter1
-
1
def param_hash
-
114
{attr.text_value => val.text_value}
-
end
-
end
-
-
1
def _nt_parameter
-
466
start_index = index
-
466
if node_cache[:parameter].has_key?(index)
-
cached = node_cache[:parameter][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
466
i0, s0 = index, []
-
466
r2 = _nt_CFWS
-
466
if r2
-
466
r1 = r2
-
else
-
r1 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
466
s0 << r1
-
466
if r1
-
466
r3 = _nt_attribute
-
466
s0 << r3
-
466
if r3
-
114
if has_terminal?("=", false, index)
-
114
r4 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
114
@index += 1
-
else
-
terminal_parse_failure("=")
-
r4 = nil
-
end
-
114
s0 << r4
-
114
if r4
-
114
r5 = _nt_value
-
114
s0 << r5
-
114
if r5
-
114
r7 = _nt_CFWS
-
114
if r7
-
114
r6 = r7
-
else
-
r6 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
114
s0 << r6
-
end
-
end
-
end
-
end
-
466
if s0.last
-
114
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
114
r0.extend(Parameter0)
-
114
r0.extend(Parameter1)
-
else
-
352
@index = i0
-
352
r0 = nil
-
end
-
-
466
node_cache[:parameter][start_index] = r0
-
-
466
r0
-
end
-
-
1
def _nt_attribute
-
466
start_index = index
-
466
if node_cache[:attribute].has_key?(index)
-
cached = node_cache[:attribute][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
466
s0, i0 = [], index
-
466
loop do
-
1302
r1 = _nt_token
-
1302
if r1
-
836
s0 << r1
-
else
-
466
break
-
end
-
end
-
466
if s0.empty?
-
352
@index = i0
-
352
r0 = nil
-
else
-
114
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
end
-
-
466
node_cache[:attribute][start_index] = r0
-
-
466
r0
-
end
-
-
1
module Value0
-
1
def text_value
-
39
quoted_content.text_value
-
end
-
end
-
-
1
def _nt_value
-
114
start_index = index
-
114
if node_cache[:value].has_key?(index)
-
cached = node_cache[:value][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
114
i0 = index
-
114
r1 = _nt_quoted_string
-
114
r1.extend(Value0)
-
114
if r1
-
39
r0 = r1
-
else
-
75
s2, i2 = [], index
-
75
loop do
-
452
i3 = index
-
452
r4 = _nt_token
-
452
if r4
-
377
r3 = r4
-
else
-
75
if has_terminal?('\G[\\x3d]', true, index)
-
r5 = true
-
@index += 1
-
else
-
75
r5 = nil
-
end
-
75
if r5
-
r3 = r5
-
else
-
75
@index = i3
-
75
r3 = nil
-
end
-
end
-
452
if r3
-
377
s2 << r3
-
else
-
75
break
-
end
-
end
-
75
if s2.empty?
-
@index = i2
-
r2 = nil
-
else
-
75
r2 = instantiate_node(SyntaxNode,input, i2...index, s2)
-
end
-
75
if r2
-
75
r0 = r2
-
else
-
@index = i0
-
r0 = nil
-
end
-
end
-
-
114
node_cache[:value][start_index] = r0
-
-
114
r0
-
end
-
-
end
-
-
1
class ContentTypeParser < Treetop::Runtime::CompiledParser
-
1
include ContentType
-
end
-
-
end
-
# Autogenerated from a Treetop grammar. Edits may be lost.
-
-
-
1
module Mail
-
1
module DateTime
-
1
include Treetop::Runtime
-
-
1
def root
-
7
@root ||= :primary
-
end
-
-
1
include RFC2822
-
-
1
module Primary0
-
1
def day_of_week
-
elements[0]
-
end
-
-
end
-
-
1
module Primary1
-
1
def date
-
7
elements[1]
-
end
-
-
1
def FWS
-
elements[2]
-
end
-
-
1
def time
-
7
elements[3]
-
end
-
-
end
-
-
1
def _nt_primary
-
7
start_index = index
-
7
if node_cache[:primary].has_key?(index)
-
cached = node_cache[:primary][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
7
i0, s0 = index, []
-
7
i2, s2 = index, []
-
7
r3 = _nt_day_of_week
-
7
s2 << r3
-
7
if r3
-
7
if has_terminal?(",", false, index)
-
7
r4 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
7
@index += 1
-
else
-
terminal_parse_failure(",")
-
r4 = nil
-
end
-
7
s2 << r4
-
end
-
7
if s2.last
-
7
r2 = instantiate_node(SyntaxNode,input, i2...index, s2)
-
7
r2.extend(Primary0)
-
else
-
@index = i2
-
r2 = nil
-
end
-
7
if r2
-
7
r1 = r2
-
else
-
r1 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
7
s0 << r1
-
7
if r1
-
7
r5 = _nt_date
-
7
s0 << r5
-
7
if r5
-
7
r6 = _nt_FWS
-
7
s0 << r6
-
7
if r6
-
7
r7 = _nt_time
-
7
s0 << r7
-
7
if r7
-
7
r9 = _nt_CFWS
-
7
if r9
-
7
r8 = r9
-
else
-
r8 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
7
s0 << r8
-
end
-
end
-
end
-
end
-
7
if s0.last
-
7
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
7
r0.extend(Primary1)
-
else
-
@index = i0
-
r0 = nil
-
end
-
-
7
node_cache[:primary][start_index] = r0
-
-
7
r0
-
end
-
-
end
-
-
1
class DateTimeParser < Treetop::Runtime::CompiledParser
-
1
include DateTime
-
end
-
-
end
-
# Autogenerated from a Treetop grammar. Edits may be lost.
-
-
-
1
module Mail
-
1
module EnvelopeFrom
-
1
include Treetop::Runtime
-
-
1
def root
-
@root ||= :primary
-
end
-
-
1
include RFC2822
-
-
1
module Primary0
-
1
def addr_spec
-
elements[0]
-
end
-
-
1
def ctime_date
-
elements[1]
-
end
-
end
-
-
1
def _nt_primary
-
start_index = index
-
if node_cache[:primary].has_key?(index)
-
cached = node_cache[:primary][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
i0, s0 = index, []
-
r1 = _nt_addr_spec
-
s0 << r1
-
if r1
-
r2 = _nt_ctime_date
-
s0 << r2
-
end
-
if s0.last
-
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
r0.extend(Primary0)
-
else
-
@index = i0
-
r0 = nil
-
end
-
-
node_cache[:primary][start_index] = r0
-
-
r0
-
end
-
-
1
module CtimeDate0
-
1
def day_name
-
elements[0]
-
end
-
-
1
def month_name
-
elements[2]
-
end
-
-
1
def day
-
elements[4]
-
end
-
-
1
def time_of_day
-
elements[6]
-
end
-
-
1
def year
-
elements[8]
-
end
-
end
-
-
1
def _nt_ctime_date
-
start_index = index
-
if node_cache[:ctime_date].has_key?(index)
-
cached = node_cache[:ctime_date][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
i0, s0 = index, []
-
r1 = _nt_day_name
-
s0 << r1
-
if r1
-
s2, i2 = [], index
-
loop do
-
if has_terminal?(" ", false, index)
-
r3 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure(" ")
-
r3 = nil
-
end
-
if r3
-
s2 << r3
-
else
-
break
-
end
-
end
-
if s2.empty?
-
@index = i2
-
r2 = nil
-
else
-
r2 = instantiate_node(SyntaxNode,input, i2...index, s2)
-
end
-
s0 << r2
-
if r2
-
r4 = _nt_month_name
-
s0 << r4
-
if r4
-
s5, i5 = [], index
-
loop do
-
if has_terminal?(" ", false, index)
-
r6 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure(" ")
-
r6 = nil
-
end
-
if r6
-
s5 << r6
-
else
-
break
-
end
-
end
-
if s5.empty?
-
@index = i5
-
r5 = nil
-
else
-
r5 = instantiate_node(SyntaxNode,input, i5...index, s5)
-
end
-
s0 << r5
-
if r5
-
r7 = _nt_day
-
s0 << r7
-
if r7
-
if has_terminal?(" ", false, index)
-
r8 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure(" ")
-
r8 = nil
-
end
-
s0 << r8
-
if r8
-
r9 = _nt_time_of_day
-
s0 << r9
-
if r9
-
if has_terminal?(" ", false, index)
-
r10 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure(" ")
-
r10 = nil
-
end
-
s0 << r10
-
if r10
-
r11 = _nt_year
-
s0 << r11
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
if s0.last
-
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
r0.extend(CtimeDate0)
-
else
-
@index = i0
-
r0 = nil
-
end
-
-
node_cache[:ctime_date][start_index] = r0
-
-
r0
-
end
-
-
end
-
-
1
class EnvelopeFromParser < Treetop::Runtime::CompiledParser
-
1
include EnvelopeFrom
-
end
-
-
end
-
# Autogenerated from a Treetop grammar. Edits may be lost.
-
-
-
1
module Mail
-
1
module MessageIds
-
1
include Treetop::Runtime
-
-
1
def root
-
70
@root ||= :primary
-
end
-
-
1
include RFC2822
-
-
1
module Primary0
-
1
def message_ids
-
68
[first_msg_id] + other_msg_ids.elements.map { |o| o.msg_id_value }
-
end
-
end
-
-
1
def _nt_primary
-
70
start_index = index
-
70
if node_cache[:primary].has_key?(index)
-
cached = node_cache[:primary][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
70
r0 = _nt_message_ids
-
70
r0.extend(Primary0)
-
-
70
node_cache[:primary][start_index] = r0
-
-
70
r0
-
end
-
-
end
-
-
1
class MessageIdsParser < Treetop::Runtime::CompiledParser
-
1
include MessageIds
-
end
-
-
end
-
# Autogenerated from a Treetop grammar. Edits may be lost.
-
-
-
1
module Mail
-
1
module MimeVersion
-
1
include Treetop::Runtime
-
-
1
def root
-
175
@root ||= :version
-
end
-
-
1
include RFC2822
-
-
1
module Version0
-
1
def CFWS1
-
elements[0]
-
end
-
-
1
def major_digits
-
175
elements[1]
-
end
-
-
1
def minor_digits
-
175
elements[5]
-
end
-
-
1
def CFWS2
-
elements[6]
-
end
-
end
-
-
1
module Version1
-
1
def major
-
175
major_digits
-
end
-
-
1
def minor
-
175
minor_digits
-
end
-
end
-
-
1
def _nt_version
-
175
start_index = index
-
175
if node_cache[:version].has_key?(index)
-
cached = node_cache[:version][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
175
i0, s0 = index, []
-
175
r1 = _nt_CFWS
-
175
s0 << r1
-
175
if r1
-
175
s2, i2 = [], index
-
175
loop do
-
350
r3 = _nt_DIGIT
-
350
if r3
-
175
s2 << r3
-
else
-
175
break
-
end
-
end
-
175
if s2.empty?
-
@index = i2
-
r2 = nil
-
else
-
175
r2 = instantiate_node(SyntaxNode,input, i2...index, s2)
-
end
-
175
s0 << r2
-
175
if r2
-
175
r5 = _nt_comment
-
175
if r5
-
r4 = r5
-
else
-
175
r4 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
175
s0 << r4
-
175
if r4
-
175
if has_terminal?(".", false, index)
-
175
r6 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
175
@index += 1
-
else
-
terminal_parse_failure(".")
-
r6 = nil
-
end
-
175
s0 << r6
-
175
if r6
-
175
r8 = _nt_comment
-
175
if r8
-
r7 = r8
-
else
-
175
r7 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
175
s0 << r7
-
175
if r7
-
175
s9, i9 = [], index
-
175
loop do
-
350
r10 = _nt_DIGIT
-
350
if r10
-
175
s9 << r10
-
else
-
175
break
-
end
-
end
-
175
if s9.empty?
-
@index = i9
-
r9 = nil
-
else
-
175
r9 = instantiate_node(SyntaxNode,input, i9...index, s9)
-
end
-
175
s0 << r9
-
175
if r9
-
175
r11 = _nt_CFWS
-
175
s0 << r11
-
end
-
end
-
end
-
end
-
end
-
end
-
175
if s0.last
-
175
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
175
r0.extend(Version0)
-
175
r0.extend(Version1)
-
else
-
@index = i0
-
r0 = nil
-
end
-
-
175
node_cache[:version][start_index] = r0
-
-
175
r0
-
end
-
-
end
-
-
1
class MimeVersionParser < Treetop::Runtime::CompiledParser
-
1
include MimeVersion
-
end
-
-
end
-
# Autogenerated from a Treetop grammar. Edits may be lost.
-
-
-
1
module Mail
-
1
module PhraseLists
-
1
include Treetop::Runtime
-
-
1
def root
-
@root ||= :primary_phrase
-
end
-
-
1
include RFC2822
-
-
1
module PrimaryPhrase0
-
1
def phrases
-
[first_phrase] + other_phrases.elements.map { |o| o.phrase_value }
-
end
-
end
-
-
1
def _nt_primary_phrase
-
start_index = index
-
if node_cache[:primary_phrase].has_key?(index)
-
cached = node_cache[:primary_phrase][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
r0 = _nt_phrase_list
-
r0.extend(PrimaryPhrase0)
-
-
node_cache[:primary_phrase][start_index] = r0
-
-
r0
-
end
-
-
end
-
-
1
class PhraseListsParser < Treetop::Runtime::CompiledParser
-
1
include PhraseLists
-
end
-
-
end
-
# Autogenerated from a Treetop grammar. Edits may be lost.
-
-
-
1
module Mail
-
1
module Received
-
1
include Treetop::Runtime
-
-
1
def root
-
@root ||= :primary
-
end
-
-
1
include RFC2822
-
-
1
module Primary0
-
1
def name_val_list
-
elements[0]
-
end
-
-
1
def date_time
-
elements[2]
-
end
-
end
-
-
1
def _nt_primary
-
start_index = index
-
if node_cache[:primary].has_key?(index)
-
cached = node_cache[:primary][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
i0, s0 = index, []
-
r1 = _nt_name_val_list
-
s0 << r1
-
if r1
-
if has_terminal?(";", false, index)
-
r2 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure(";")
-
r2 = nil
-
end
-
s0 << r2
-
if r2
-
r3 = _nt_date_time
-
s0 << r3
-
end
-
end
-
if s0.last
-
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
r0.extend(Primary0)
-
else
-
@index = i0
-
r0 = nil
-
end
-
-
node_cache[:primary][start_index] = r0
-
-
r0
-
end
-
-
end
-
-
1
class ReceivedParser < Treetop::Runtime::CompiledParser
-
1
include Received
-
end
-
-
end
-
# Autogenerated from a Treetop grammar. Edits may be lost.
-
-
-
1
module Mail
-
1
module RFC2045
-
1
include Treetop::Runtime
-
-
1
def root
-
@root ||= :tspecials
-
end
-
-
1
def _nt_tspecials
-
start_index = index
-
if node_cache[:tspecials].has_key?(index)
-
cached = node_cache[:tspecials][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
i0 = index
-
if has_terminal?("(", false, index)
-
r1 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure("(")
-
r1 = nil
-
end
-
if r1
-
r0 = r1
-
else
-
if has_terminal?(")", false, index)
-
r2 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure(")")
-
r2 = nil
-
end
-
if r2
-
r0 = r2
-
else
-
if has_terminal?("<", false, index)
-
r3 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure("<")
-
r3 = nil
-
end
-
if r3
-
r0 = r3
-
else
-
if has_terminal?(">", false, index)
-
r4 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure(">")
-
r4 = nil
-
end
-
if r4
-
r0 = r4
-
else
-
if has_terminal?("@", false, index)
-
r5 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure("@")
-
r5 = nil
-
end
-
if r5
-
r0 = r5
-
else
-
if has_terminal?(",", false, index)
-
r6 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure(",")
-
r6 = nil
-
end
-
if r6
-
r0 = r6
-
else
-
if has_terminal?(";", false, index)
-
r7 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure(";")
-
r7 = nil
-
end
-
if r7
-
r0 = r7
-
else
-
if has_terminal?(":", false, index)
-
r8 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure(":")
-
r8 = nil
-
end
-
if r8
-
r0 = r8
-
else
-
if has_terminal?('\\', false, index)
-
r9 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure('\\')
-
r9 = nil
-
end
-
if r9
-
r0 = r9
-
else
-
if has_terminal?("<", false, index)
-
r10 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure("<")
-
r10 = nil
-
end
-
if r10
-
r0 = r10
-
else
-
if has_terminal?(">", false, index)
-
r11 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure(">")
-
r11 = nil
-
end
-
if r11
-
r0 = r11
-
else
-
if has_terminal?("/", false, index)
-
r12 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure("/")
-
r12 = nil
-
end
-
if r12
-
r0 = r12
-
else
-
if has_terminal?("[", false, index)
-
r13 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure("[")
-
r13 = nil
-
end
-
if r13
-
r0 = r13
-
else
-
if has_terminal?("]", false, index)
-
r14 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure("]")
-
r14 = nil
-
end
-
if r14
-
r0 = r14
-
else
-
if has_terminal?("?", false, index)
-
r15 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure("?")
-
r15 = nil
-
end
-
if r15
-
r0 = r15
-
else
-
if has_terminal?("=", false, index)
-
r16 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure("=")
-
r16 = nil
-
end
-
if r16
-
r0 = r16
-
else
-
@index = i0
-
r0 = nil
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
-
node_cache[:tspecials][start_index] = r0
-
-
r0
-
end
-
-
1
def _nt_ietf_token
-
516
start_index = index
-
516
if node_cache[:ietf_token].has_key?(index)
-
cached = node_cache[:ietf_token][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
516
i0 = index
-
516
if has_terminal?("7bit", false, index)
-
80
r1 = instantiate_node(SyntaxNode,input, index...(index + 4))
-
80
@index += 4
-
else
-
436
terminal_parse_failure("7bit")
-
436
r1 = nil
-
end
-
516
if r1
-
80
r0 = r1
-
else
-
436
if has_terminal?("8bit", false, index)
-
r2 = instantiate_node(SyntaxNode,input, index...(index + 4))
-
@index += 4
-
else
-
436
terminal_parse_failure("8bit")
-
436
r2 = nil
-
end
-
436
if r2
-
r0 = r2
-
else
-
436
if has_terminal?("binary", false, index)
-
12
r3 = instantiate_node(SyntaxNode,input, index...(index + 6))
-
12
@index += 6
-
else
-
424
terminal_parse_failure("binary")
-
424
r3 = nil
-
end
-
436
if r3
-
12
r0 = r3
-
else
-
424
if has_terminal?("quoted-printable", false, index)
-
r4 = instantiate_node(SyntaxNode,input, index...(index + 16))
-
@index += 16
-
else
-
424
terminal_parse_failure("quoted-printable")
-
424
r4 = nil
-
end
-
424
if r4
-
r0 = r4
-
else
-
424
if has_terminal?("base64", false, index)
-
7
r5 = instantiate_node(SyntaxNode,input, index...(index + 6))
-
7
@index += 6
-
else
-
417
terminal_parse_failure("base64")
-
417
r5 = nil
-
end
-
424
if r5
-
7
r0 = r5
-
else
-
417
@index = i0
-
417
r0 = nil
-
end
-
end
-
end
-
end
-
end
-
-
516
node_cache[:ietf_token][start_index] = r0
-
-
516
r0
-
end
-
-
1
module CustomXToken0
-
end
-
-
1
def _nt_custom_x_token
-
417
start_index = index
-
417
if node_cache[:custom_x_token].has_key?(index)
-
cached = node_cache[:custom_x_token][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
417
i0, s0 = index, []
-
417
if has_terminal?('\G[xX]', true, index)
-
2
r1 = true
-
2
@index += 1
-
else
-
415
r1 = nil
-
end
-
417
s0 << r1
-
417
if r1
-
2
if has_terminal?("-", false, index)
-
2
r2 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
2
@index += 1
-
else
-
terminal_parse_failure("-")
-
r2 = nil
-
end
-
2
s0 << r2
-
2
if r2
-
2
s3, i3 = [], index
-
2
loop do
-
8
r4 = _nt_token
-
8
if r4
-
6
s3 << r4
-
else
-
2
break
-
end
-
end
-
2
if s3.empty?
-
@index = i3
-
r3 = nil
-
else
-
2
r3 = instantiate_node(SyntaxNode,input, i3...index, s3)
-
end
-
2
s0 << r3
-
end
-
end
-
417
if s0.last
-
2
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
2
r0.extend(CustomXToken0)
-
else
-
415
@index = i0
-
415
r0 = nil
-
end
-
-
417
node_cache[:custom_x_token][start_index] = r0
-
-
417
r0
-
end
-
-
1
def _nt_iana_token
-
350
start_index = index
-
350
if node_cache[:iana_token].has_key?(index)
-
cached = node_cache[:iana_token][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
350
s0, i0 = [], index
-
350
loop do
-
2045
r1 = _nt_token
-
2045
if r1
-
1695
s0 << r1
-
else
-
350
break
-
end
-
end
-
350
if s0.empty?
-
@index = i0
-
r0 = nil
-
else
-
350
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
end
-
-
350
node_cache[:iana_token][start_index] = r0
-
-
350
r0
-
end
-
-
1
def _nt_token
-
3915
start_index = index
-
3915
if node_cache[:token].has_key?(index)
-
342
cached = node_cache[:token][index]
-
342
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
342
return cached
-
end
-
-
3573
i0 = index
-
3573
if has_terminal?('\G[\\x21-\\x27]', true, index)
-
r1 = true
-
@index += 1
-
else
-
3573
r1 = nil
-
end
-
3573
if r1
-
r0 = r1
-
else
-
3573
if has_terminal?('\G[\\x2a-\\x2b]', true, index)
-
r2 = true
-
@index += 1
-
else
-
3573
r2 = nil
-
end
-
3573
if r2
-
r0 = r2
-
else
-
3573
if has_terminal?('\G[\\x2c-\\x2e]', true, index)
-
74
r3 = true
-
74
@index += 1
-
else
-
3499
r3 = nil
-
end
-
3573
if r3
-
74
r0 = r3
-
else
-
3499
if has_terminal?('\G[\\x30-\\x39]', true, index)
-
73
r4 = true
-
73
@index += 1
-
else
-
3426
r4 = nil
-
end
-
3499
if r4
-
73
r0 = r4
-
else
-
3426
if has_terminal?('\G[\\x41-\\x5a]', true, index)
-
224
r5 = true
-
224
@index += 1
-
else
-
3202
r5 = nil
-
end
-
3426
if r5
-
224
r0 = r5
-
else
-
3202
if has_terminal?('\G[\\x5e-\\x7e]', true, index)
-
2639
r6 = true
-
2639
@index += 1
-
else
-
563
r6 = nil
-
end
-
3202
if r6
-
2639
r0 = r6
-
else
-
563
@index = i0
-
563
r0 = nil
-
end
-
end
-
end
-
end
-
end
-
end
-
-
3573
node_cache[:token][start_index] = r0
-
-
3573
r0
-
end
-
-
end
-
-
1
class RFC2045Parser < Treetop::Runtime::CompiledParser
-
1
include RFC2045
-
end
-
-
end
-
# Autogenerated from a Treetop grammar. Edits may be lost.
-
-
-
1
module Mail
-
1
module RFC2822
-
1
include Treetop::Runtime
-
-
1
def root
-
@root ||= :ALPHA
-
end
-
-
1
include RFC2822Obsolete
-
-
1
def _nt_ALPHA
-
9851
start_index = index
-
9851
if node_cache[:ALPHA].has_key?(index)
-
cached = node_cache[:ALPHA][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
9851
if has_terminal?('\G[a-zA-Z]', true, index)
-
7067
r0 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
7067
@index += 1
-
else
-
2784
r0 = nil
-
end
-
-
9851
node_cache[:ALPHA][start_index] = r0
-
-
9851
r0
-
end
-
-
1
def _nt_DIGIT
-
3596
start_index = index
-
3596
if node_cache[:DIGIT].has_key?(index)
-
cached = node_cache[:DIGIT][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
3596
if has_terminal?('\G[0-9]', true, index)
-
1802
r0 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
1802
@index += 1
-
else
-
1794
r0 = nil
-
end
-
-
3596
node_cache[:DIGIT][start_index] = r0
-
-
3596
r0
-
end
-
-
1
def _nt_DQUOTE
-
3090
start_index = index
-
3090
if node_cache[:DQUOTE].has_key?(index)
-
576
cached = node_cache[:DQUOTE][index]
-
576
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
576
return cached
-
end
-
-
2514
if has_terminal?('"', false, index)
-
102
r0 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
102
@index += 1
-
else
-
2412
terminal_parse_failure('"')
-
2412
r0 = nil
-
end
-
-
2514
node_cache[:DQUOTE][start_index] = r0
-
-
2514
r0
-
end
-
-
1
def _nt_LF
-
start_index = index
-
if node_cache[:LF].has_key?(index)
-
cached = node_cache[:LF][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
if has_terminal?("\n", false, index)
-
r0 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure("\n")
-
r0 = nil
-
end
-
-
node_cache[:LF][start_index] = r0
-
-
r0
-
end
-
-
1
def _nt_CR
-
start_index = index
-
if node_cache[:CR].has_key?(index)
-
cached = node_cache[:CR][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
if has_terminal?("\r", false, index)
-
r0 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure("\r")
-
r0 = nil
-
end
-
-
node_cache[:CR][start_index] = r0
-
-
r0
-
end
-
-
1
def _nt_CRLF
-
10719
start_index = index
-
10719
if node_cache[:CRLF].has_key?(index)
-
5405
cached = node_cache[:CRLF][index]
-
5405
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
5405
return cached
-
end
-
-
5314
if has_terminal?("\r\n", false, index)
-
r0 = instantiate_node(SyntaxNode,input, index...(index + 2))
-
@index += 2
-
else
-
5314
terminal_parse_failure("\r\n")
-
5314
r0 = nil
-
end
-
-
5314
node_cache[:CRLF][start_index] = r0
-
-
5314
r0
-
end
-
-
1
def _nt_WSP
-
10886
start_index = index
-
10886
if node_cache[:WSP].has_key?(index)
-
5572
cached = node_cache[:WSP][index]
-
5572
if cached
-
167
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
167
@index = cached.interval.end
-
end
-
5572
return cached
-
end
-
-
5314
if has_terminal?('\G[\\x09\\x20]', true, index)
-
167
r0 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
167
@index += 1
-
else
-
5147
r0 = nil
-
end
-
-
5314
node_cache[:WSP][start_index] = r0
-
-
5314
r0
-
end
-
-
1
module FWS0
-
1
def CRLF
-
elements[1]
-
end
-
-
end
-
-
1
module FWS1
-
1
def CRLF
-
elements[0]
-
end
-
-
end
-
-
1
def _nt_FWS
-
9200
start_index = index
-
9200
if node_cache[:FWS].has_key?(index)
-
3924
cached = node_cache[:FWS][index]
-
3924
if cached
-
128
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
128
@index = cached.interval.end
-
end
-
3924
return cached
-
end
-
-
5276
i0 = index
-
5276
i1, s1 = index, []
-
5276
s2, i2 = [], index
-
5276
loop do
-
5443
r3 = _nt_WSP
-
5443
if r3
-
167
s2 << r3
-
else
-
5276
break
-
end
-
end
-
5276
r2 = instantiate_node(SyntaxNode,input, i2...index, s2)
-
5276
s1 << r2
-
5276
if r2
-
5276
r4 = _nt_CRLF
-
5276
s1 << r4
-
5276
if r4
-
s5, i5 = [], index
-
loop do
-
r6 = _nt_WSP
-
if r6
-
s5 << r6
-
else
-
break
-
end
-
end
-
if s5.empty?
-
@index = i5
-
r5 = nil
-
else
-
r5 = instantiate_node(SyntaxNode,input, i5...index, s5)
-
end
-
s1 << r5
-
end
-
end
-
5276
if s1.last
-
r1 = instantiate_node(SyntaxNode,input, i1...index, s1)
-
r1.extend(FWS0)
-
else
-
5276
@index = i1
-
5276
r1 = nil
-
end
-
5276
if r1
-
r0 = r1
-
else
-
5276
i7, s7 = index, []
-
5276
r8 = _nt_CRLF
-
5276
s7 << r8
-
5276
if r8
-
s9, i9 = [], index
-
loop do
-
r10 = _nt_WSP
-
if r10
-
s9 << r10
-
else
-
break
-
end
-
end
-
if s9.empty?
-
@index = i9
-
r9 = nil
-
else
-
r9 = instantiate_node(SyntaxNode,input, i9...index, s9)
-
end
-
s7 << r9
-
end
-
5276
if s7.last
-
r7 = instantiate_node(SyntaxNode,input, i7...index, s7)
-
r7.extend(FWS1)
-
else
-
5276
@index = i7
-
5276
r7 = nil
-
end
-
5276
if r7
-
r0 = r7
-
else
-
5276
r11 = _nt_obs_FWS
-
5276
if r11
-
167
r0 = r11
-
else
-
5109
@index = i0
-
5109
r0 = nil
-
end
-
end
-
end
-
-
5276
node_cache[:FWS][start_index] = r0
-
-
5276
r0
-
end
-
-
1
module CFWS0
-
1
def comment
-
elements[1]
-
end
-
end
-
-
1
module CFWS1
-
end
-
-
1
def _nt_CFWS
-
8913
start_index = index
-
8913
if node_cache[:CFWS].has_key?(index)
-
5332
cached = node_cache[:CFWS][index]
-
5332
if cached
-
5332
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
5332
@index = cached.interval.end
-
end
-
5332
return cached
-
end
-
-
3581
i0, s0 = index, []
-
3581
s1, i1 = [], index
-
3581
loop do
-
3582
i2, s2 = index, []
-
3582
s3, i3 = [], index
-
3582
loop do
-
3711
r4 = _nt_FWS
-
3711
if r4
-
129
s3 << r4
-
else
-
3582
break
-
end
-
end
-
3582
r3 = instantiate_node(SyntaxNode,input, i3...index, s3)
-
3582
s2 << r3
-
3582
if r3
-
3582
r5 = _nt_comment
-
3582
s2 << r5
-
end
-
3582
if s2.last
-
1
r2 = instantiate_node(SyntaxNode,input, i2...index, s2)
-
1
r2.extend(CFWS0)
-
else
-
3581
@index = i2
-
3581
r2 = nil
-
end
-
3582
if r2
-
1
s1 << r2
-
else
-
3581
break
-
end
-
end
-
3581
r1 = instantiate_node(SyntaxNode,input, i1...index, s1)
-
3581
s0 << r1
-
3581
if r1
-
3581
r7 = _nt_FWS
-
3581
if r7
-
128
r6 = r7
-
else
-
3453
r6 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
3581
s0 << r6
-
end
-
3581
if s0.last
-
3581
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
3581
r0.extend(CFWS1)
-
else
-
@index = i0
-
r0 = nil
-
end
-
-
3581
node_cache[:CFWS][start_index] = r0
-
-
3581
r0
-
end
-
-
1
def _nt_NO_WS_CTL
-
1525
start_index = index
-
1525
if node_cache[:NO_WS_CTL].has_key?(index)
-
cached = node_cache[:NO_WS_CTL][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
1525
i0 = index
-
1525
if has_terminal?('\G[\\x01-\\x08]', true, index)
-
r1 = true
-
@index += 1
-
else
-
1525
r1 = nil
-
end
-
1525
if r1
-
r0 = r1
-
else
-
1525
if has_terminal?('\G[\\x0B-\\x0C]', true, index)
-
r2 = true
-
@index += 1
-
else
-
1525
r2 = nil
-
end
-
1525
if r2
-
r0 = r2
-
else
-
1525
if has_terminal?('\G[\\x0E-\\x1F]', true, index)
-
r3 = true
-
@index += 1
-
else
-
1525
r3 = nil
-
end
-
1525
if r3
-
r0 = r3
-
else
-
1525
if has_terminal?('\G[\\x7f]', true, index)
-
r4 = true
-
@index += 1
-
else
-
1525
r4 = nil
-
end
-
1525
if r4
-
r0 = r4
-
else
-
1525
@index = i0
-
1525
r0 = nil
-
end
-
end
-
end
-
end
-
-
1525
node_cache[:NO_WS_CTL][start_index] = r0
-
-
1525
r0
-
end
-
-
1
def _nt_specials
-
start_index = index
-
if node_cache[:specials].has_key?(index)
-
cached = node_cache[:specials][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
i0 = index
-
if has_terminal?("(", false, index)
-
r1 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure("(")
-
r1 = nil
-
end
-
if r1
-
r0 = r1
-
else
-
if has_terminal?(")", false, index)
-
r2 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure(")")
-
r2 = nil
-
end
-
if r2
-
r0 = r2
-
else
-
if has_terminal?("<", false, index)
-
r3 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure("<")
-
r3 = nil
-
end
-
if r3
-
r0 = r3
-
else
-
if has_terminal?(">", false, index)
-
r4 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure(">")
-
r4 = nil
-
end
-
if r4
-
r0 = r4
-
else
-
if has_terminal?("[", false, index)
-
r5 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure("[")
-
r5 = nil
-
end
-
if r5
-
r0 = r5
-
else
-
if has_terminal?("]", false, index)
-
r6 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure("]")
-
r6 = nil
-
end
-
if r6
-
r0 = r6
-
else
-
if has_terminal?(":", false, index)
-
r7 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure(":")
-
r7 = nil
-
end
-
if r7
-
r0 = r7
-
else
-
if has_terminal?(";", false, index)
-
r8 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure(";")
-
r8 = nil
-
end
-
if r8
-
r0 = r8
-
else
-
if has_terminal?("@", false, index)
-
r9 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure("@")
-
r9 = nil
-
end
-
if r9
-
r0 = r9
-
else
-
if has_terminal?('\\', false, index)
-
r10 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure('\\')
-
r10 = nil
-
end
-
if r10
-
r0 = r10
-
else
-
if has_terminal?(",", false, index)
-
r11 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure(",")
-
r11 = nil
-
end
-
if r11
-
r0 = r11
-
else
-
if has_terminal?(".", false, index)
-
r12 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure(".")
-
r12 = nil
-
end
-
if r12
-
r0 = r12
-
else
-
r13 = _nt_DQUOTE
-
if r13
-
r0 = r13
-
else
-
@index = i0
-
r0 = nil
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
-
node_cache[:specials][start_index] = r0
-
-
r0
-
end
-
-
1
def _nt_ctext
-
26
start_index = index
-
26
if node_cache[:ctext].has_key?(index)
-
cached = node_cache[:ctext][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
26
i0 = index
-
26
r1 = _nt_NO_WS_CTL
-
26
if r1
-
r0 = r1
-
else
-
26
if has_terminal?('\G[\\x21-\\x27]', true, index)
-
r2 = true
-
@index += 1
-
else
-
26
r2 = nil
-
end
-
26
if r2
-
r0 = r2
-
else
-
26
if has_terminal?('\G[\\x2a-\\x5b]', true, index)
-
5
r3 = true
-
5
@index += 1
-
else
-
21
r3 = nil
-
end
-
26
if r3
-
5
r0 = r3
-
else
-
21
if has_terminal?('\G[\\x5d-\\x7e]', true, index)
-
20
r4 = true
-
20
@index += 1
-
else
-
1
r4 = nil
-
end
-
21
if r4
-
20
r0 = r4
-
else
-
1
@index = i0
-
1
r0 = nil
-
end
-
end
-
end
-
end
-
-
26
node_cache[:ctext][start_index] = r0
-
-
26
r0
-
end
-
-
1
def _nt_ccontent
-
26
start_index = index
-
26
if node_cache[:ccontent].has_key?(index)
-
cached = node_cache[:ccontent][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
26
i0 = index
-
26
r1 = _nt_ctext
-
26
if r1
-
25
r0 = r1
-
else
-
1
r2 = _nt_quoted_pair
-
1
if r2
-
r0 = r2
-
else
-
1
r3 = _nt_comment
-
1
if r3
-
r0 = r3
-
else
-
1
@index = i0
-
1
r0 = nil
-
end
-
end
-
end
-
-
26
node_cache[:ccontent][start_index] = r0
-
-
26
r0
-
end
-
-
1
module Comment0
-
1
def ccontent
-
elements[1]
-
end
-
end
-
-
1
module Comment1
-
end
-
-
1
def _nt_comment
-
3933
start_index = index
-
3933
if node_cache[:comment].has_key?(index)
-
2
cached = node_cache[:comment][index]
-
2
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
2
return cached
-
end
-
-
3931
i0, s0 = index, []
-
3931
if has_terminal?("(", false, index)
-
1
r1 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
1
@index += 1
-
else
-
3930
terminal_parse_failure("(")
-
3930
r1 = nil
-
end
-
3931
s0 << r1
-
3931
if r1
-
1
s2, i2 = [], index
-
1
loop do
-
26
i3, s3 = index, []
-
26
r5 = _nt_FWS
-
26
if r5
-
3
r4 = r5
-
else
-
23
r4 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
26
s3 << r4
-
26
if r4
-
26
r6 = _nt_ccontent
-
26
s3 << r6
-
end
-
26
if s3.last
-
25
r3 = instantiate_node(SyntaxNode,input, i3...index, s3)
-
25
r3.extend(Comment0)
-
else
-
1
@index = i3
-
1
r3 = nil
-
end
-
26
if r3
-
25
s2 << r3
-
else
-
1
break
-
end
-
end
-
1
r2 = instantiate_node(SyntaxNode,input, i2...index, s2)
-
1
s0 << r2
-
1
if r2
-
1
r8 = _nt_FWS
-
1
if r8
-
r7 = r8
-
else
-
1
r7 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
1
s0 << r7
-
1
if r7
-
1
if has_terminal?(")", false, index)
-
1
r9 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
1
@index += 1
-
else
-
terminal_parse_failure(")")
-
r9 = nil
-
end
-
1
s0 << r9
-
end
-
end
-
end
-
3931
if s0.last
-
1
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
1
r0.extend(Comment1)
-
else
-
3930
@index = i0
-
3930
r0 = nil
-
end
-
-
3931
node_cache[:comment][start_index] = r0
-
-
3931
r0
-
end
-
-
1
def _nt_atext
-
17307
start_index = index
-
17307
if node_cache[:atext].has_key?(index)
-
8614
cached = node_cache[:atext][index]
-
8614
if cached
-
5832
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
5832
@index = cached.interval.end
-
end
-
8614
return cached
-
end
-
-
8693
i0 = index
-
8693
r1 = _nt_ALPHA
-
8693
if r1
-
6163
r0 = r1
-
else
-
2530
r2 = _nt_DIGIT
-
2530
if r2
-
1329
r0 = r2
-
else
-
1201
if has_terminal?("!", false, index)
-
r3 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
1201
terminal_parse_failure("!")
-
1201
r3 = nil
-
end
-
1201
if r3
-
r0 = r3
-
else
-
1201
if has_terminal?("#", false, index)
-
r4 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
1201
terminal_parse_failure("#")
-
1201
r4 = nil
-
end
-
1201
if r4
-
r0 = r4
-
else
-
1201
if has_terminal?("$", false, index)
-
r5 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
1201
terminal_parse_failure("$")
-
1201
r5 = nil
-
end
-
1201
if r5
-
r0 = r5
-
else
-
1201
if has_terminal?("%", false, index)
-
r6 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
1201
terminal_parse_failure("%")
-
1201
r6 = nil
-
end
-
1201
if r6
-
r0 = r6
-
else
-
1201
if has_terminal?("&", false, index)
-
r7 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
1201
terminal_parse_failure("&")
-
1201
r7 = nil
-
end
-
1201
if r7
-
r0 = r7
-
else
-
1201
if has_terminal?("'", false, index)
-
r8 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
1201
terminal_parse_failure("'")
-
1201
r8 = nil
-
end
-
1201
if r8
-
r0 = r8
-
else
-
1201
if has_terminal?("*", false, index)
-
r9 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
1201
terminal_parse_failure("*")
-
1201
r9 = nil
-
end
-
1201
if r9
-
r0 = r9
-
else
-
1201
if has_terminal?("+", false, index)
-
r10 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
1201
terminal_parse_failure("+")
-
1201
r10 = nil
-
end
-
1201
if r10
-
r0 = r10
-
else
-
1201
if has_terminal?("-", false, index)
-
1
r11 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
1
@index += 1
-
else
-
1200
terminal_parse_failure("-")
-
1200
r11 = nil
-
end
-
1201
if r11
-
1
r0 = r11
-
else
-
1200
if has_terminal?("/", false, index)
-
r12 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
1200
terminal_parse_failure("/")
-
1200
r12 = nil
-
end
-
1200
if r12
-
r0 = r12
-
else
-
1200
if has_terminal?("=", false, index)
-
r13 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
1200
terminal_parse_failure("=")
-
1200
r13 = nil
-
end
-
1200
if r13
-
r0 = r13
-
else
-
1200
if has_terminal?("?", false, index)
-
r14 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
1200
terminal_parse_failure("?")
-
1200
r14 = nil
-
end
-
1200
if r14
-
r0 = r14
-
else
-
1200
if has_terminal?("^", false, index)
-
r15 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
1200
terminal_parse_failure("^")
-
1200
r15 = nil
-
end
-
1200
if r15
-
r0 = r15
-
else
-
1200
if has_terminal?("_", false, index)
-
58
r16 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
58
@index += 1
-
else
-
1142
terminal_parse_failure("_")
-
1142
r16 = nil
-
end
-
1200
if r16
-
58
r0 = r16
-
else
-
1142
if has_terminal?("`", false, index)
-
r17 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
1142
terminal_parse_failure("`")
-
1142
r17 = nil
-
end
-
1142
if r17
-
r0 = r17
-
else
-
1142
if has_terminal?("{", false, index)
-
r18 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
1142
terminal_parse_failure("{")
-
1142
r18 = nil
-
end
-
1142
if r18
-
r0 = r18
-
else
-
1142
if has_terminal?("|", false, index)
-
r19 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
1142
terminal_parse_failure("|")
-
1142
r19 = nil
-
end
-
1142
if r19
-
r0 = r19
-
else
-
1142
if has_terminal?("}", false, index)
-
r20 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
1142
terminal_parse_failure("}")
-
1142
r20 = nil
-
end
-
1142
if r20
-
r0 = r20
-
else
-
1142
if has_terminal?("~", false, index)
-
r21 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
1142
terminal_parse_failure("~")
-
1142
r21 = nil
-
end
-
1142
if r21
-
r0 = r21
-
else
-
1142
@index = i0
-
1142
r0 = nil
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
-
8693
node_cache[:atext][start_index] = r0
-
-
8693
r0
-
end
-
-
1
def _nt_mtext
-
136
start_index = index
-
136
if node_cache[:mtext].has_key?(index)
-
cached = node_cache[:mtext][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
136
s0, i0 = [], index
-
136
loop do
-
1829
i1 = index
-
1829
r2 = _nt_atext
-
1829
if r2
-
1693
r1 = r2
-
else
-
136
if has_terminal?(".", false, index)
-
r3 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
136
terminal_parse_failure(".")
-
136
r3 = nil
-
end
-
136
if r3
-
r1 = r3
-
else
-
136
@index = i1
-
136
r1 = nil
-
end
-
end
-
1829
if r1
-
1693
s0 << r1
-
else
-
136
break
-
end
-
end
-
136
if s0.empty?
-
68
@index = i0
-
68
r0 = nil
-
else
-
68
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
end
-
-
136
node_cache[:mtext][start_index] = r0
-
-
136
r0
-
end
-
-
1
module Atom0
-
end
-
-
1
def _nt_atom
-
2139
start_index = index
-
2139
if node_cache[:atom].has_key?(index)
-
cached = node_cache[:atom][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
2139
i0, s0 = index, []
-
2139
r2 = _nt_CFWS
-
2139
if r2
-
2139
r1 = r2
-
else
-
r1 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
2139
s0 << r1
-
2139
if r1
-
2139
s3, i3 = [], index
-
2139
loop do
-
7980
r4 = _nt_atext
-
7980
if r4
-
5841
s3 << r4
-
else
-
2139
break
-
end
-
end
-
2139
if s3.empty?
-
1069
@index = i3
-
1069
r3 = nil
-
else
-
1070
r3 = instantiate_node(SyntaxNode,input, i3...index, s3)
-
end
-
2139
s0 << r3
-
2139
if r3
-
1070
r6 = _nt_CFWS
-
1070
if r6
-
1070
r5 = r6
-
else
-
r5 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
1070
s0 << r5
-
end
-
end
-
2139
if s0.last
-
1070
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
1070
r0.extend(Atom0)
-
else
-
1069
@index = i0
-
1069
r0 = nil
-
end
-
-
2139
node_cache[:atom][start_index] = r0
-
-
2139
r0
-
end
-
-
1
module DotAtom0
-
1
def dot_atom_text
-
elements[1]
-
end
-
-
end
-
-
1
def _nt_dot_atom
-
289
start_index = index
-
289
if node_cache[:dot_atom].has_key?(index)
-
cached = node_cache[:dot_atom][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
289
i0, s0 = index, []
-
289
r2 = _nt_CFWS
-
289
if r2
-
289
r1 = r2
-
else
-
r1 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
289
s0 << r1
-
289
if r1
-
289
r3 = _nt_dot_atom_text
-
289
s0 << r3
-
289
if r3
-
289
r5 = _nt_CFWS
-
289
if r5
-
289
r4 = r5
-
else
-
r4 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
289
s0 << r4
-
end
-
end
-
289
if s0.last
-
289
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
289
r0.extend(DotAtom0)
-
else
-
@index = i0
-
r0 = nil
-
end
-
-
289
node_cache[:dot_atom][start_index] = r0
-
-
289
r0
-
end
-
-
1
module LocalDotAtom0
-
1
def local_dot_atom_text
-
elements[1]
-
end
-
-
end
-
-
1
def _nt_local_dot_atom
-
289
start_index = index
-
289
if node_cache[:local_dot_atom].has_key?(index)
-
cached = node_cache[:local_dot_atom][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
289
i0, s0 = index, []
-
289
r2 = _nt_CFWS
-
289
if r2
-
289
r1 = r2
-
else
-
r1 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
289
s0 << r1
-
289
if r1
-
289
r3 = _nt_local_dot_atom_text
-
289
s0 << r3
-
289
if r3
-
289
r5 = _nt_CFWS
-
289
if r5
-
289
r4 = r5
-
else
-
r4 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
289
s0 << r4
-
end
-
end
-
289
if s0.last
-
289
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
289
r0.extend(LocalDotAtom0)
-
else
-
@index = i0
-
r0 = nil
-
end
-
-
289
node_cache[:local_dot_atom][start_index] = r0
-
-
289
r0
-
end
-
-
1
def _nt_message_id_text
-
68
start_index = index
-
68
if node_cache[:message_id_text].has_key?(index)
-
cached = node_cache[:message_id_text][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
68
s0, i0 = [], index
-
68
loop do
-
136
r1 = _nt_mtext
-
136
if r1
-
68
s0 << r1
-
else
-
68
break
-
end
-
end
-
68
if s0.empty?
-
@index = i0
-
r0 = nil
-
else
-
68
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
end
-
-
68
node_cache[:message_id_text][start_index] = r0
-
-
68
r0
-
end
-
-
1
module DotAtomText0
-
1
def domain_text
-
elements[0]
-
end
-
-
end
-
-
1
def _nt_dot_atom_text
-
289
start_index = index
-
289
if node_cache[:dot_atom_text].has_key?(index)
-
cached = node_cache[:dot_atom_text][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
289
s0, i0 = [], index
-
289
loop do
-
1070
i1, s1 = index, []
-
1070
r2 = _nt_domain_text
-
1070
s1 << r2
-
1070
if r2
-
781
if has_terminal?(".", false, index)
-
492
r4 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
492
@index += 1
-
else
-
289
terminal_parse_failure(".")
-
289
r4 = nil
-
end
-
781
if r4
-
492
r3 = r4
-
else
-
289
r3 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
781
s1 << r3
-
end
-
1070
if s1.last
-
781
r1 = instantiate_node(SyntaxNode,input, i1...index, s1)
-
781
r1.extend(DotAtomText0)
-
else
-
289
@index = i1
-
289
r1 = nil
-
end
-
1070
if r1
-
781
s0 << r1
-
else
-
289
break
-
end
-
end
-
289
if s0.empty?
-
@index = i0
-
r0 = nil
-
else
-
289
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
end
-
-
289
node_cache[:dot_atom_text][start_index] = r0
-
-
289
r0
-
end
-
-
1
module LocalDotAtomText0
-
1
def domain_text
-
elements[1]
-
end
-
end
-
-
1
def _nt_local_dot_atom_text
-
289
start_index = index
-
289
if node_cache[:local_dot_atom_text].has_key?(index)
-
cached = node_cache[:local_dot_atom_text][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
289
s0, i0 = [], index
-
289
loop do
-
579
i1, s1 = index, []
-
579
s2, i2 = [], index
-
579
loop do
-
580
if has_terminal?(".", false, index)
-
1
r3 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
1
@index += 1
-
else
-
579
terminal_parse_failure(".")
-
579
r3 = nil
-
end
-
580
if r3
-
1
s2 << r3
-
else
-
579
break
-
end
-
end
-
579
r2 = instantiate_node(SyntaxNode,input, i2...index, s2)
-
579
s1 << r2
-
579
if r2
-
579
r4 = _nt_domain_text
-
579
s1 << r4
-
end
-
579
if s1.last
-
290
r1 = instantiate_node(SyntaxNode,input, i1...index, s1)
-
290
r1.extend(LocalDotAtomText0)
-
else
-
289
@index = i1
-
289
r1 = nil
-
end
-
579
if r1
-
290
s0 << r1
-
else
-
289
break
-
end
-
end
-
289
if s0.empty?
-
@index = i0
-
r0 = nil
-
else
-
289
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
end
-
-
289
node_cache[:local_dot_atom_text][start_index] = r0
-
-
289
r0
-
end
-
-
1
module DomainText0
-
1
def quoted_domain
-
elements[1]
-
end
-
end
-
-
1
module DomainText1
-
1
def DQUOTE1
-
elements[0]
-
end
-
-
1
def DQUOTE2
-
elements[3]
-
end
-
end
-
-
1
def _nt_domain_text
-
1649
start_index = index
-
1649
if node_cache[:domain_text].has_key?(index)
-
cached = node_cache[:domain_text][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
1649
i0 = index
-
1649
i1, s1 = index, []
-
1649
r2 = _nt_DQUOTE
-
1649
s1 << r2
-
1649
if r2
-
s3, i3 = [], index
-
loop do
-
i4, s4 = index, []
-
r6 = _nt_FWS
-
if r6
-
r5 = r6
-
else
-
r5 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
s4 << r5
-
if r5
-
r7 = _nt_quoted_domain
-
s4 << r7
-
end
-
if s4.last
-
r4 = instantiate_node(SyntaxNode,input, i4...index, s4)
-
r4.extend(DomainText0)
-
else
-
@index = i4
-
r4 = nil
-
end
-
if r4
-
s3 << r4
-
else
-
break
-
end
-
end
-
if s3.empty?
-
@index = i3
-
r3 = nil
-
else
-
r3 = instantiate_node(SyntaxNode,input, i3...index, s3)
-
end
-
s1 << r3
-
if r3
-
r9 = _nt_FWS
-
if r9
-
r8 = r9
-
else
-
r8 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
s1 << r8
-
if r8
-
r10 = _nt_DQUOTE
-
s1 << r10
-
end
-
end
-
end
-
1649
if s1.last
-
r1 = instantiate_node(SyntaxNode,input, i1...index, s1)
-
r1.extend(DomainText1)
-
else
-
1649
@index = i1
-
1649
r1 = nil
-
end
-
1649
if r1
-
r0 = r1
-
else
-
1649
s11, i11 = [], index
-
1649
loop do
-
7498
r12 = _nt_atext
-
7498
if r12
-
5849
s11 << r12
-
else
-
1649
break
-
end
-
end
-
1649
if s11.empty?
-
578
@index = i11
-
578
r11 = nil
-
else
-
1071
r11 = instantiate_node(SyntaxNode,input, i11...index, s11)
-
end
-
1649
if r11
-
1071
r0 = r11
-
else
-
578
@index = i0
-
578
r0 = nil
-
end
-
end
-
-
1649
node_cache[:domain_text][start_index] = r0
-
-
1649
r0
-
end
-
-
1
module QuotedDomain0
-
1
def text
-
elements[1]
-
end
-
end
-
-
1
def _nt_quoted_domain
-
start_index = index
-
if node_cache[:quoted_domain].has_key?(index)
-
cached = node_cache[:quoted_domain][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
i0 = index
-
r1 = _nt_qdcontent
-
if r1
-
r0 = r1
-
else
-
i2, s2 = index, []
-
if has_terminal?("\\", false, index)
-
r3 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure("\\")
-
r3 = nil
-
end
-
s2 << r3
-
if r3
-
r4 = _nt_text
-
s2 << r4
-
end
-
if s2.last
-
r2 = instantiate_node(SyntaxNode,input, i2...index, s2)
-
r2.extend(QuotedDomain0)
-
else
-
@index = i2
-
r2 = nil
-
end
-
if r2
-
r0 = r2
-
else
-
@index = i0
-
r0 = nil
-
end
-
end
-
-
node_cache[:quoted_domain][start_index] = r0
-
-
r0
-
end
-
-
1
def _nt_qdcontent
-
start_index = index
-
if node_cache[:qdcontent].has_key?(index)
-
cached = node_cache[:qdcontent][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
i0 = index
-
r1 = _nt_NO_WS_CTL
-
if r1
-
r0 = r1
-
else
-
if has_terminal?('\G[\\x21]', true, index)
-
r2 = true
-
@index += 1
-
else
-
r2 = nil
-
end
-
if r2
-
r0 = r2
-
else
-
if has_terminal?('\G[\\x23-\\x45]', true, index)
-
r3 = true
-
@index += 1
-
else
-
r3 = nil
-
end
-
if r3
-
r0 = r3
-
else
-
if has_terminal?('\G[\\x47-\\x5b]', true, index)
-
r4 = true
-
@index += 1
-
else
-
r4 = nil
-
end
-
if r4
-
r0 = r4
-
else
-
if has_terminal?('\G[\\x5d-\\x7e]', true, index)
-
r5 = true
-
@index += 1
-
else
-
r5 = nil
-
end
-
if r5
-
r0 = r5
-
else
-
@index = i0
-
r0 = nil
-
end
-
end
-
end
-
end
-
end
-
-
node_cache[:qdcontent][start_index] = r0
-
-
r0
-
end
-
-
1
def _nt_phrase
-
289
start_index = index
-
289
if node_cache[:phrase].has_key?(index)
-
cached = node_cache[:phrase][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
289
i0 = index
-
289
r1 = _nt_obs_phrase
-
289
if r1
-
289
r0 = r1
-
else
-
s2, i2 = [], index
-
loop do
-
r3 = _nt_word
-
if r3
-
s2 << r3
-
else
-
break
-
end
-
end
-
if s2.empty?
-
@index = i2
-
r2 = nil
-
else
-
r2 = instantiate_node(SyntaxNode,input, i2...index, s2)
-
end
-
if r2
-
r0 = r2
-
else
-
@index = i0
-
r0 = nil
-
end
-
end
-
-
289
node_cache[:phrase][start_index] = r0
-
-
289
r0
-
end
-
-
1
def _nt_word
-
2139
start_index = index
-
2139
if node_cache[:word].has_key?(index)
-
cached = node_cache[:word][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
2139
i0 = index
-
2139
r1 = _nt_atom
-
2139
if r1
-
1070
r0 = r1
-
else
-
1069
r2 = _nt_quoted_string
-
1069
if r2
-
r0 = r2
-
else
-
1069
@index = i0
-
1069
r0 = nil
-
end
-
end
-
-
2139
node_cache[:word][start_index] = r0
-
-
2139
r0
-
end
-
-
1
module PhraseList0
-
1
def phrase_value
-
elements[2]
-
end
-
end
-
-
1
module PhraseList1
-
1
def first_phrase
-
elements[0]
-
end
-
-
1
def other_phrases
-
elements[1]
-
end
-
end
-
-
1
def _nt_phrase_list
-
start_index = index
-
if node_cache[:phrase_list].has_key?(index)
-
cached = node_cache[:phrase_list][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
i0, s0 = index, []
-
r1 = _nt_phrase
-
s0 << r1
-
if r1
-
s2, i2 = [], index
-
loop do
-
i3, s3 = index, []
-
if has_terminal?(",", false, index)
-
r4 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure(",")
-
r4 = nil
-
end
-
s3 << r4
-
if r4
-
s5, i5 = [], index
-
loop do
-
r6 = _nt_FWS
-
if r6
-
s5 << r6
-
else
-
break
-
end
-
end
-
r5 = instantiate_node(SyntaxNode,input, i5...index, s5)
-
s3 << r5
-
if r5
-
r7 = _nt_phrase
-
s3 << r7
-
end
-
end
-
if s3.last
-
r3 = instantiate_node(SyntaxNode,input, i3...index, s3)
-
r3.extend(PhraseList0)
-
else
-
@index = i3
-
r3 = nil
-
end
-
if r3
-
s2 << r3
-
else
-
break
-
end
-
end
-
r2 = instantiate_node(SyntaxNode,input, i2...index, s2)
-
s0 << r2
-
end
-
if s0.last
-
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
r0.extend(PhraseList1)
-
else
-
@index = i0
-
r0 = nil
-
end
-
-
node_cache[:phrase_list][start_index] = r0
-
-
r0
-
end
-
-
1
module DomainLiteral0
-
1
def dcontent
-
elements[1]
-
end
-
end
-
-
1
module DomainLiteral1
-
end
-
-
1
def _nt_domain_literal
-
start_index = index
-
if node_cache[:domain_literal].has_key?(index)
-
cached = node_cache[:domain_literal][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
i0, s0 = index, []
-
r2 = _nt_CFWS
-
if r2
-
r1 = r2
-
else
-
r1 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
s0 << r1
-
if r1
-
if has_terminal?("[", false, index)
-
r3 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure("[")
-
r3 = nil
-
end
-
s0 << r3
-
if r3
-
s4, i4 = [], index
-
loop do
-
i5, s5 = index, []
-
r7 = _nt_FWS
-
if r7
-
r6 = r7
-
else
-
r6 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
s5 << r6
-
if r6
-
r8 = _nt_dcontent
-
s5 << r8
-
end
-
if s5.last
-
r5 = instantiate_node(SyntaxNode,input, i5...index, s5)
-
r5.extend(DomainLiteral0)
-
else
-
@index = i5
-
r5 = nil
-
end
-
if r5
-
s4 << r5
-
else
-
break
-
end
-
end
-
r4 = instantiate_node(SyntaxNode,input, i4...index, s4)
-
s0 << r4
-
if r4
-
r10 = _nt_FWS
-
if r10
-
r9 = r10
-
else
-
r9 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
s0 << r9
-
if r9
-
if has_terminal?("]", false, index)
-
r11 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure("]")
-
r11 = nil
-
end
-
s0 << r11
-
if r11
-
r13 = _nt_CFWS
-
if r13
-
r12 = r13
-
else
-
r12 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
s0 << r12
-
end
-
end
-
end
-
end
-
end
-
if s0.last
-
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
r0.extend(DomainLiteral1)
-
else
-
@index = i0
-
r0 = nil
-
end
-
-
node_cache[:domain_literal][start_index] = r0
-
-
r0
-
end
-
-
1
def _nt_dcontent
-
start_index = index
-
if node_cache[:dcontent].has_key?(index)
-
cached = node_cache[:dcontent][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
i0 = index
-
r1 = _nt_dtext
-
if r1
-
r0 = r1
-
else
-
r2 = _nt_quoted_pair
-
if r2
-
r0 = r2
-
else
-
@index = i0
-
r0 = nil
-
end
-
end
-
-
node_cache[:dcontent][start_index] = r0
-
-
r0
-
end
-
-
1
def _nt_dtext
-
start_index = index
-
if node_cache[:dtext].has_key?(index)
-
cached = node_cache[:dtext][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
i0 = index
-
r1 = _nt_NO_WS_CTL
-
if r1
-
r0 = r1
-
else
-
if has_terminal?('\G[\\x21-\\x5a]', true, index)
-
r2 = true
-
@index += 1
-
else
-
r2 = nil
-
end
-
if r2
-
r0 = r2
-
else
-
if has_terminal?('\G[\\x5e-\\x7e]', true, index)
-
r3 = true
-
@index += 1
-
else
-
r3 = nil
-
end
-
if r3
-
r0 = r3
-
else
-
@index = i0
-
r0 = nil
-
end
-
end
-
end
-
-
node_cache[:dtext][start_index] = r0
-
-
r0
-
end
-
-
1
module AngleAddr0
-
1
def addr_spec
-
25
elements[2]
-
end
-
-
end
-
-
1
def _nt_angle_addr
-
577
start_index = index
-
577
if node_cache[:angle_addr].has_key?(index)
-
cached = node_cache[:angle_addr][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
577
i0 = index
-
577
i1, s1 = index, []
-
577
r3 = _nt_CFWS
-
577
if r3
-
577
r2 = r3
-
else
-
r2 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
577
s1 << r2
-
577
if r2
-
577
if has_terminal?("<", false, index)
-
1
r4 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
1
@index += 1
-
else
-
576
terminal_parse_failure("<")
-
576
r4 = nil
-
end
-
577
s1 << r4
-
577
if r4
-
1
r5 = _nt_addr_spec
-
1
s1 << r5
-
1
if r5
-
1
if has_terminal?(">", false, index)
-
1
r6 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
1
@index += 1
-
else
-
terminal_parse_failure(">")
-
r6 = nil
-
end
-
1
s1 << r6
-
1
if r6
-
1
r8 = _nt_CFWS
-
1
if r8
-
1
r7 = r8
-
else
-
r7 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
1
s1 << r7
-
end
-
end
-
end
-
end
-
577
if s1.last
-
1
r1 = instantiate_node(SyntaxNode,input, i1...index, s1)
-
1
r1.extend(AngleAddr0)
-
else
-
576
@index = i1
-
576
r1 = nil
-
end
-
577
if r1
-
1
r0 = r1
-
else
-
576
r9 = _nt_obs_angle_addr
-
576
if r9
-
r0 = r9
-
else
-
576
@index = i0
-
576
r0 = nil
-
end
-
end
-
-
577
node_cache[:angle_addr][start_index] = r0
-
-
577
r0
-
end
-
-
1
module AddrSpec0
-
1
def local_part
-
772
elements[0]
-
end
-
-
1
def domain
-
103
elements[2]
-
end
-
end
-
-
1
def _nt_addr_spec
-
289
start_index = index
-
289
if node_cache[:addr_spec].has_key?(index)
-
cached = node_cache[:addr_spec][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
289
i0 = index
-
289
i1, s1 = index, []
-
289
r2 = _nt_local_part
-
289
s1 << r2
-
289
if r2
-
289
if has_terminal?("@", false, index)
-
289
r3 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
289
@index += 1
-
else
-
terminal_parse_failure("@")
-
r3 = nil
-
end
-
289
s1 << r3
-
289
if r3
-
289
r4 = _nt_domain
-
289
s1 << r4
-
end
-
end
-
289
if s1.last
-
289
r1 = instantiate_node(SyntaxNode,input, i1...index, s1)
-
289
r1.extend(AddrSpec0)
-
else
-
@index = i1
-
r1 = nil
-
end
-
289
if r1
-
289
r0 = r1
-
else
-
r5 = _nt_local_part
-
if r5
-
r0 = r5
-
else
-
@index = i0
-
r0 = nil
-
end
-
end
-
-
289
node_cache[:addr_spec][start_index] = r0
-
-
289
r0
-
end
-
-
1
def _nt_local_part
-
289
start_index = index
-
289
if node_cache[:local_part].has_key?(index)
-
cached = node_cache[:local_part][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
289
i0 = index
-
289
r1 = _nt_local_dot_atom
-
289
if r1
-
289
r0 = r1
-
else
-
r2 = _nt_quoted_string
-
if r2
-
r0 = r2
-
else
-
r3 = _nt_obs_local_part
-
if r3
-
r0 = r3
-
else
-
@index = i0
-
r0 = nil
-
end
-
end
-
end
-
-
289
node_cache[:local_part][start_index] = r0
-
-
289
r0
-
end
-
-
1
def _nt_domain
-
289
start_index = index
-
289
if node_cache[:domain].has_key?(index)
-
cached = node_cache[:domain][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
289
i0 = index
-
289
r1 = _nt_dot_atom
-
289
if r1
-
289
r0 = r1
-
else
-
r2 = _nt_domain_literal
-
if r2
-
r0 = r2
-
else
-
r3 = _nt_obs_domain
-
if r3
-
r0 = r3
-
else
-
@index = i0
-
r0 = nil
-
end
-
end
-
end
-
-
289
node_cache[:domain][start_index] = r0
-
-
289
r0
-
end
-
-
1
module Group0
-
1
def group_name
-
elements[0]
-
end
-
-
1
def group_list
-
elements[2]
-
end
-
-
end
-
-
1
def _nt_group
-
289
start_index = index
-
289
if node_cache[:group].has_key?(index)
-
cached = node_cache[:group][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
289
i0, s0 = index, []
-
289
r1 = _nt_display_name
-
289
s0 << r1
-
289
if r1
-
289
if has_terminal?(":", false, index)
-
r2 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
289
terminal_parse_failure(":")
-
289
r2 = nil
-
end
-
289
s0 << r2
-
289
if r2
-
i4 = index
-
r5 = _nt_mailbox_list_group
-
if r5
-
r4 = r5
-
else
-
r6 = _nt_CFWS
-
if r6
-
r4 = r6
-
else
-
@index = i4
-
r4 = nil
-
end
-
end
-
if r4
-
r3 = r4
-
else
-
r3 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
s0 << r3
-
if r3
-
if has_terminal?(";", false, index)
-
r7 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure(";")
-
r7 = nil
-
end
-
s0 << r7
-
if r7
-
r9 = _nt_CFWS
-
if r9
-
r8 = r9
-
else
-
r8 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
s0 << r8
-
end
-
end
-
end
-
end
-
289
if s0.last
-
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
r0.extend(Group0)
-
else
-
289
@index = i0
-
289
r0 = nil
-
end
-
-
289
node_cache[:group][start_index] = r0
-
-
289
r0
-
end
-
-
1
module MailboxListGroup0
-
1
def addresses
-
[first_addr] + other_addr.elements.map { |o| o.addr_value }
-
end
-
end
-
-
1
def _nt_mailbox_list_group
-
start_index = index
-
if node_cache[:mailbox_list_group].has_key?(index)
-
cached = node_cache[:mailbox_list_group][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
r0 = _nt_mailbox_list
-
r0.extend(MailboxListGroup0)
-
-
node_cache[:mailbox_list_group][start_index] = r0
-
-
r0
-
end
-
-
1
module QuotedString0
-
1
def qcontent
-
elements[1]
-
end
-
end
-
-
1
module QuotedString1
-
1
def DQUOTE1
-
elements[1]
-
end
-
-
1
def quoted_content
-
51
elements[2]
-
end
-
-
1
def DQUOTE2
-
elements[4]
-
end
-
-
end
-
-
1
def _nt_quoted_string
-
1195
start_index = index
-
1195
if node_cache[:quoted_string].has_key?(index)
-
cached = node_cache[:quoted_string][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
1195
i0, s0 = index, []
-
1195
r2 = _nt_CFWS
-
1195
if r2
-
1195
r1 = r2
-
else
-
r1 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
1195
s0 << r1
-
1195
if r1
-
1195
r3 = _nt_DQUOTE
-
1195
s0 << r3
-
1195
if r3
-
51
s4, i4 = [], index
-
51
loop do
-
1499
i5, s5 = index, []
-
1499
r7 = _nt_FWS
-
1499
if r7
-
r6 = r7
-
else
-
1499
r6 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
1499
s5 << r6
-
1499
if r6
-
1499
r8 = _nt_qcontent
-
1499
s5 << r8
-
end
-
1499
if s5.last
-
1448
r5 = instantiate_node(SyntaxNode,input, i5...index, s5)
-
1448
r5.extend(QuotedString0)
-
else
-
51
@index = i5
-
51
r5 = nil
-
end
-
1499
if r5
-
1448
s4 << r5
-
else
-
51
break
-
end
-
end
-
51
if s4.empty?
-
@index = i4
-
r4 = nil
-
else
-
51
r4 = instantiate_node(SyntaxNode,input, i4...index, s4)
-
end
-
51
s0 << r4
-
51
if r4
-
51
r10 = _nt_FWS
-
51
if r10
-
r9 = r10
-
else
-
51
r9 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
51
s0 << r9
-
51
if r9
-
51
r11 = _nt_DQUOTE
-
51
s0 << r11
-
51
if r11
-
51
r13 = _nt_CFWS
-
51
if r13
-
51
r12 = r13
-
else
-
r12 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
51
s0 << r12
-
end
-
end
-
end
-
end
-
end
-
1195
if s0.last
-
51
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
51
r0.extend(QuotedString1)
-
else
-
1144
@index = i0
-
1144
r0 = nil
-
end
-
-
1195
node_cache[:quoted_string][start_index] = r0
-
-
1195
r0
-
end
-
-
1
def _nt_qcontent
-
1499
start_index = index
-
1499
if node_cache[:qcontent].has_key?(index)
-
cached = node_cache[:qcontent][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
1499
i0 = index
-
1499
r1 = _nt_qtext
-
1499
if r1
-
1448
r0 = r1
-
else
-
51
r2 = _nt_quoted_pair
-
51
if r2
-
r0 = r2
-
else
-
51
@index = i0
-
51
r0 = nil
-
end
-
end
-
-
1499
node_cache[:qcontent][start_index] = r0
-
-
1499
r0
-
end
-
-
1
module QuotedPair0
-
1
def text
-
elements[1]
-
end
-
end
-
-
1
def _nt_quoted_pair
-
52
start_index = index
-
52
if node_cache[:quoted_pair].has_key?(index)
-
cached = node_cache[:quoted_pair][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
52
i0 = index
-
52
i1, s1 = index, []
-
52
if has_terminal?("\\", false, index)
-
r2 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
52
terminal_parse_failure("\\")
-
52
r2 = nil
-
end
-
52
s1 << r2
-
52
if r2
-
r3 = _nt_text
-
s1 << r3
-
end
-
52
if s1.last
-
r1 = instantiate_node(SyntaxNode,input, i1...index, s1)
-
r1.extend(QuotedPair0)
-
else
-
52
@index = i1
-
52
r1 = nil
-
end
-
52
if r1
-
r0 = r1
-
else
-
52
r4 = _nt_obs_qp
-
52
if r4
-
r0 = r4
-
else
-
52
@index = i0
-
52
r0 = nil
-
end
-
end
-
-
52
node_cache[:quoted_pair][start_index] = r0
-
-
52
r0
-
end
-
-
1
def _nt_qtext
-
1499
start_index = index
-
1499
if node_cache[:qtext].has_key?(index)
-
cached = node_cache[:qtext][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
1499
i0 = index
-
1499
r1 = _nt_NO_WS_CTL
-
1499
if r1
-
r0 = r1
-
else
-
1499
if has_terminal?('\G[\\x21]', true, index)
-
r2 = true
-
@index += 1
-
else
-
1499
r2 = nil
-
end
-
1499
if r2
-
r0 = r2
-
else
-
1499
if has_terminal?('\G[\\x23-\\x5b]', true, index)
-
792
r3 = true
-
792
@index += 1
-
else
-
707
r3 = nil
-
end
-
1499
if r3
-
792
r0 = r3
-
else
-
707
if has_terminal?('\G[\\x5d-\\x7e]', true, index)
-
656
r4 = true
-
656
@index += 1
-
else
-
51
r4 = nil
-
end
-
707
if r4
-
656
r0 = r4
-
else
-
51
@index = i0
-
51
r0 = nil
-
end
-
end
-
end
-
end
-
-
1499
node_cache[:qtext][start_index] = r0
-
-
1499
r0
-
end
-
-
1
def _nt_text
-
start_index = index
-
if node_cache[:text].has_key?(index)
-
cached = node_cache[:text][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
i0 = index
-
if has_terminal?('\G[\\x01-\\x09]', true, index)
-
r1 = true
-
@index += 1
-
else
-
r1 = nil
-
end
-
if r1
-
r0 = r1
-
else
-
if has_terminal?('\G[\\x0b-\\x0c]', true, index)
-
r2 = true
-
@index += 1
-
else
-
r2 = nil
-
end
-
if r2
-
r0 = r2
-
else
-
if has_terminal?('\G[\\x0e-\\x7e]', true, index)
-
r3 = true
-
@index += 1
-
else
-
r3 = nil
-
end
-
if r3
-
r0 = r3
-
else
-
r4 = _nt_obs_text
-
if r4
-
r0 = r4
-
else
-
@index = i0
-
r0 = nil
-
end
-
end
-
end
-
end
-
-
node_cache[:text][start_index] = r0
-
-
r0
-
end
-
-
1
def _nt_display_name
-
578
start_index = index
-
578
if node_cache[:display_name].has_key?(index)
-
289
cached = node_cache[:display_name][index]
-
289
if cached
-
289
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
289
@index = cached.interval.end
-
end
-
289
return cached
-
end
-
-
289
r0 = _nt_phrase
-
-
289
node_cache[:display_name][start_index] = r0
-
-
289
r0
-
end
-
-
1
module NameAddr0
-
1
def display_name
-
1
elements[0]
-
end
-
-
1
def angle_addr
-
46
elements[1]
-
end
-
end
-
-
1
def _nt_name_addr
-
289
start_index = index
-
289
if node_cache[:name_addr].has_key?(index)
-
cached = node_cache[:name_addr][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
289
i0 = index
-
289
i1, s1 = index, []
-
289
r2 = _nt_display_name
-
289
s1 << r2
-
289
if r2
-
289
r3 = _nt_angle_addr
-
289
s1 << r3
-
end
-
289
if s1.last
-
1
r1 = instantiate_node(SyntaxNode,input, i1...index, s1)
-
1
r1.extend(NameAddr0)
-
else
-
288
@index = i1
-
288
r1 = nil
-
end
-
289
if r1
-
1
r0 = r1
-
else
-
288
r4 = _nt_angle_addr
-
288
if r4
-
r0 = r4
-
else
-
288
@index = i0
-
288
r0 = nil
-
end
-
end
-
-
289
node_cache[:name_addr][start_index] = r0
-
-
289
r0
-
end
-
-
1
module MailboxList0
-
1
def addr_value
-
elements[1]
-
end
-
end
-
-
1
module MailboxList1
-
1
def first_addr
-
elements[0]
-
end
-
-
1
def other_addr
-
elements[1]
-
end
-
end
-
-
1
def _nt_mailbox_list
-
start_index = index
-
if node_cache[:mailbox_list].has_key?(index)
-
cached = node_cache[:mailbox_list][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
i0 = index
-
i1, s1 = index, []
-
r2 = _nt_mailbox
-
s1 << r2
-
if r2
-
s3, i3 = [], index
-
loop do
-
i4, s4 = index, []
-
if has_terminal?(",", false, index)
-
r5 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure(",")
-
r5 = nil
-
end
-
s4 << r5
-
if r5
-
r6 = _nt_mailbox
-
s4 << r6
-
end
-
if s4.last
-
r4 = instantiate_node(SyntaxNode,input, i4...index, s4)
-
r4.extend(MailboxList0)
-
else
-
@index = i4
-
r4 = nil
-
end
-
if r4
-
s3 << r4
-
else
-
break
-
end
-
end
-
r3 = instantiate_node(SyntaxNode,input, i3...index, s3)
-
s1 << r3
-
end
-
if s1.last
-
r1 = instantiate_node(SyntaxNode,input, i1...index, s1)
-
r1.extend(MailboxList1)
-
else
-
@index = i1
-
r1 = nil
-
end
-
if r1
-
r0 = r1
-
else
-
r7 = _nt_obs_mbox_list
-
if r7
-
r0 = r7
-
else
-
@index = i0
-
r0 = nil
-
end
-
end
-
-
node_cache[:mailbox_list][start_index] = r0
-
-
r0
-
end
-
-
1
def _nt_mailbox
-
289
start_index = index
-
289
if node_cache[:mailbox].has_key?(index)
-
cached = node_cache[:mailbox][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
289
i0 = index
-
289
r1 = _nt_name_addr
-
289
if r1
-
1
r0 = r1
-
else
-
288
r2 = _nt_addr_spec
-
288
if r2
-
288
r0 = r2
-
else
-
@index = i0
-
r0 = nil
-
end
-
end
-
-
289
node_cache[:mailbox][start_index] = r0
-
-
289
r0
-
end
-
-
1
module Address0
-
-
1
def dig_comments(comments, elements)
-
elements.each { |elem|
-
if elem.respond_to?(:comment)
-
comments << elem.comment
-
end
-
dig_comments(comments, elem.elements) if elem.elements
-
}
-
end
-
-
1
def comments
-
comments = []
-
dig_comments(comments, elements)
-
comments
-
end
-
end
-
-
1
module Address1
-
-
1
def dig_comments(comments, elements)
-
29523
elements.each { |elem|
-
66697
if elem.respond_to?(:comment)
-
comments << elem.comment
-
end
-
66697
dig_comments(comments, elem.elements) if elem.elements
-
}
-
end
-
-
1
def comments
-
1367
comments = []
-
1367
dig_comments(comments, elements)
-
1367
comments
-
end
-
end
-
-
1
def _nt_address
-
289
start_index = index
-
289
if node_cache[:address].has_key?(index)
-
cached = node_cache[:address][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
289
i0 = index
-
289
r1 = _nt_group
-
289
r1.extend(Address0)
-
289
if r1
-
r0 = r1
-
else
-
289
r2 = _nt_mailbox
-
289
r2.extend(Address1)
-
289
if r2
-
289
r0 = r2
-
else
-
@index = i0
-
r0 = nil
-
end
-
end
-
-
289
node_cache[:address][start_index] = r0
-
-
289
r0
-
end
-
-
1
module AddressList0
-
1
def addr_value
-
elements[3]
-
end
-
end
-
-
1
module AddressList1
-
1
def first_addr
-
289
elements[0]
-
end
-
-
1
def other_addr
-
289
elements[1]
-
end
-
end
-
-
1
def _nt_address_list
-
289
start_index = index
-
289
if node_cache[:address_list].has_key?(index)
-
cached = node_cache[:address_list][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
289
i0, s0 = index, []
-
289
r2 = _nt_address
-
289
if r2
-
289
r1 = r2
-
else
-
r1 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
289
s0 << r1
-
289
if r1
-
289
s3, i3 = [], index
-
289
loop do
-
289
i4, s4 = index, []
-
289
s5, i5 = [], index
-
289
loop do
-
289
r6 = _nt_FWS
-
289
if r6
-
s5 << r6
-
else
-
289
break
-
end
-
end
-
289
r5 = instantiate_node(SyntaxNode,input, i5...index, s5)
-
289
s4 << r5
-
289
if r5
-
289
if has_terminal?(",", false, index)
-
r7 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
289
terminal_parse_failure(",")
-
289
r7 = nil
-
end
-
289
s4 << r7
-
289
if r7
-
s8, i8 = [], index
-
loop do
-
r9 = _nt_FWS
-
if r9
-
s8 << r9
-
else
-
break
-
end
-
end
-
r8 = instantiate_node(SyntaxNode,input, i8...index, s8)
-
s4 << r8
-
if r8
-
r11 = _nt_address
-
if r11
-
r10 = r11
-
else
-
r10 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
s4 << r10
-
end
-
end
-
end
-
289
if s4.last
-
r4 = instantiate_node(SyntaxNode,input, i4...index, s4)
-
r4.extend(AddressList0)
-
else
-
289
@index = i4
-
289
r4 = nil
-
end
-
289
if r4
-
s3 << r4
-
else
-
289
break
-
end
-
end
-
289
r3 = instantiate_node(SyntaxNode,input, i3...index, s3)
-
289
s0 << r3
-
end
-
289
if s0.last
-
289
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
289
r0.extend(AddressList1)
-
else
-
@index = i0
-
r0 = nil
-
end
-
-
289
node_cache[:address_list][start_index] = r0
-
-
289
r0
-
end
-
-
1
module DateTime0
-
1
def day_of_week
-
elements[0]
-
end
-
-
end
-
-
1
module DateTime1
-
1
def date
-
elements[1]
-
end
-
-
1
def FWS
-
elements[2]
-
end
-
-
1
def time
-
elements[3]
-
end
-
-
end
-
-
1
def _nt_date_time
-
start_index = index
-
if node_cache[:date_time].has_key?(index)
-
cached = node_cache[:date_time][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
i0, s0 = index, []
-
i2, s2 = index, []
-
r3 = _nt_day_of_week
-
s2 << r3
-
if r3
-
if has_terminal?(",", false, index)
-
r4 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure(",")
-
r4 = nil
-
end
-
s2 << r4
-
end
-
if s2.last
-
r2 = instantiate_node(SyntaxNode,input, i2...index, s2)
-
r2.extend(DateTime0)
-
else
-
@index = i2
-
r2 = nil
-
end
-
if r2
-
r1 = r2
-
else
-
r1 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
s0 << r1
-
if r1
-
r5 = _nt_date
-
s0 << r5
-
if r5
-
r6 = _nt_FWS
-
s0 << r6
-
if r6
-
r7 = _nt_time
-
s0 << r7
-
if r7
-
r9 = _nt_CFWS
-
if r9
-
r8 = r9
-
else
-
r8 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
s0 << r8
-
end
-
end
-
end
-
end
-
if s0.last
-
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
r0.extend(DateTime1)
-
else
-
@index = i0
-
r0 = nil
-
end
-
-
node_cache[:date_time][start_index] = r0
-
-
r0
-
end
-
-
1
module DayOfWeek0
-
1
def day_name
-
elements[1]
-
end
-
end
-
-
1
def _nt_day_of_week
-
7
start_index = index
-
7
if node_cache[:day_of_week].has_key?(index)
-
cached = node_cache[:day_of_week][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
7
i0 = index
-
7
i1, s1 = index, []
-
7
r3 = _nt_FWS
-
7
if r3
-
r2 = r3
-
else
-
7
r2 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
7
s1 << r2
-
7
if r2
-
7
r4 = _nt_day_name
-
7
s1 << r4
-
end
-
7
if s1.last
-
7
r1 = instantiate_node(SyntaxNode,input, i1...index, s1)
-
7
r1.extend(DayOfWeek0)
-
else
-
@index = i1
-
r1 = nil
-
end
-
7
if r1
-
7
r0 = r1
-
else
-
r5 = _nt_obs_day_of_week
-
if r5
-
r0 = r5
-
else
-
@index = i0
-
r0 = nil
-
end
-
end
-
-
7
node_cache[:day_of_week][start_index] = r0
-
-
7
r0
-
end
-
-
1
def _nt_day_name
-
7
start_index = index
-
7
if node_cache[:day_name].has_key?(index)
-
cached = node_cache[:day_name][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
7
i0 = index
-
7
if has_terminal?("Mon", false, index)
-
1
r1 = instantiate_node(SyntaxNode,input, index...(index + 3))
-
1
@index += 3
-
else
-
6
terminal_parse_failure("Mon")
-
6
r1 = nil
-
end
-
7
if r1
-
1
r0 = r1
-
else
-
6
if has_terminal?("Tue", false, index)
-
4
r2 = instantiate_node(SyntaxNode,input, index...(index + 3))
-
4
@index += 3
-
else
-
2
terminal_parse_failure("Tue")
-
2
r2 = nil
-
end
-
6
if r2
-
4
r0 = r2
-
else
-
2
if has_terminal?("Wed", false, index)
-
r3 = instantiate_node(SyntaxNode,input, index...(index + 3))
-
@index += 3
-
else
-
2
terminal_parse_failure("Wed")
-
2
r3 = nil
-
end
-
2
if r3
-
r0 = r3
-
else
-
2
if has_terminal?("Thu", false, index)
-
r4 = instantiate_node(SyntaxNode,input, index...(index + 3))
-
@index += 3
-
else
-
2
terminal_parse_failure("Thu")
-
2
r4 = nil
-
end
-
2
if r4
-
r0 = r4
-
else
-
2
if has_terminal?("Fri", false, index)
-
r5 = instantiate_node(SyntaxNode,input, index...(index + 3))
-
@index += 3
-
else
-
2
terminal_parse_failure("Fri")
-
2
r5 = nil
-
end
-
2
if r5
-
r0 = r5
-
else
-
2
if has_terminal?("Sat", false, index)
-
r6 = instantiate_node(SyntaxNode,input, index...(index + 3))
-
@index += 3
-
else
-
2
terminal_parse_failure("Sat")
-
2
r6 = nil
-
end
-
2
if r6
-
r0 = r6
-
else
-
2
if has_terminal?("Sun", false, index)
-
2
r7 = instantiate_node(SyntaxNode,input, index...(index + 3))
-
2
@index += 3
-
else
-
terminal_parse_failure("Sun")
-
r7 = nil
-
end
-
2
if r7
-
2
r0 = r7
-
else
-
@index = i0
-
r0 = nil
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
-
7
node_cache[:day_name][start_index] = r0
-
-
7
r0
-
end
-
-
1
module Date0
-
1
def day
-
elements[0]
-
end
-
-
1
def month
-
elements[1]
-
end
-
-
1
def year
-
elements[2]
-
end
-
end
-
-
1
def _nt_date
-
7
start_index = index
-
7
if node_cache[:date].has_key?(index)
-
cached = node_cache[:date][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
7
i0, s0 = index, []
-
7
r1 = _nt_day
-
7
s0 << r1
-
7
if r1
-
7
r2 = _nt_month
-
7
s0 << r2
-
7
if r2
-
7
r3 = _nt_year
-
7
s0 << r3
-
end
-
end
-
7
if s0.last
-
7
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
7
r0.extend(Date0)
-
else
-
@index = i0
-
r0 = nil
-
end
-
-
7
node_cache[:date][start_index] = r0
-
-
7
r0
-
end
-
-
1
module Year0
-
1
def DIGIT1
-
elements[0]
-
end
-
-
1
def DIGIT2
-
elements[1]
-
end
-
-
1
def DIGIT3
-
elements[2]
-
end
-
-
1
def DIGIT4
-
elements[3]
-
end
-
end
-
-
1
def _nt_year
-
7
start_index = index
-
7
if node_cache[:year].has_key?(index)
-
cached = node_cache[:year][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
7
i0 = index
-
7
i1, s1 = index, []
-
7
r2 = _nt_DIGIT
-
7
s1 << r2
-
7
if r2
-
7
r3 = _nt_DIGIT
-
7
s1 << r3
-
7
if r3
-
7
r4 = _nt_DIGIT
-
7
s1 << r4
-
7
if r4
-
7
r5 = _nt_DIGIT
-
7
s1 << r5
-
end
-
end
-
end
-
7
if s1.last
-
7
r1 = instantiate_node(SyntaxNode,input, i1...index, s1)
-
7
r1.extend(Year0)
-
else
-
@index = i1
-
r1 = nil
-
end
-
7
if r1
-
7
r0 = r1
-
else
-
r6 = _nt_obs_year
-
if r6
-
r0 = r6
-
else
-
@index = i0
-
r0 = nil
-
end
-
end
-
-
7
node_cache[:year][start_index] = r0
-
-
7
r0
-
end
-
-
1
module Month0
-
1
def FWS1
-
elements[0]
-
end
-
-
1
def month_name
-
elements[1]
-
end
-
-
1
def FWS2
-
elements[2]
-
end
-
end
-
-
1
def _nt_month
-
7
start_index = index
-
7
if node_cache[:month].has_key?(index)
-
cached = node_cache[:month][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
7
i0 = index
-
7
i1, s1 = index, []
-
7
r2 = _nt_FWS
-
7
s1 << r2
-
7
if r2
-
7
r3 = _nt_month_name
-
7
s1 << r3
-
7
if r3
-
7
r4 = _nt_FWS
-
7
s1 << r4
-
end
-
end
-
7
if s1.last
-
7
r1 = instantiate_node(SyntaxNode,input, i1...index, s1)
-
7
r1.extend(Month0)
-
else
-
@index = i1
-
r1 = nil
-
end
-
7
if r1
-
7
r0 = r1
-
else
-
r5 = _nt_obs_month
-
if r5
-
r0 = r5
-
else
-
@index = i0
-
r0 = nil
-
end
-
end
-
-
7
node_cache[:month][start_index] = r0
-
-
7
r0
-
end
-
-
1
def _nt_month_name
-
7
start_index = index
-
7
if node_cache[:month_name].has_key?(index)
-
cached = node_cache[:month_name][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
7
i0 = index
-
7
if has_terminal?("Jan", false, index)
-
r1 = instantiate_node(SyntaxNode,input, index...(index + 3))
-
@index += 3
-
else
-
7
terminal_parse_failure("Jan")
-
7
r1 = nil
-
end
-
7
if r1
-
r0 = r1
-
else
-
7
if has_terminal?("Feb", false, index)
-
r2 = instantiate_node(SyntaxNode,input, index...(index + 3))
-
@index += 3
-
else
-
7
terminal_parse_failure("Feb")
-
7
r2 = nil
-
end
-
7
if r2
-
r0 = r2
-
else
-
7
if has_terminal?("Mar", false, index)
-
r3 = instantiate_node(SyntaxNode,input, index...(index + 3))
-
@index += 3
-
else
-
7
terminal_parse_failure("Mar")
-
7
r3 = nil
-
end
-
7
if r3
-
r0 = r3
-
else
-
7
if has_terminal?("Apr", false, index)
-
r4 = instantiate_node(SyntaxNode,input, index...(index + 3))
-
@index += 3
-
else
-
7
terminal_parse_failure("Apr")
-
7
r4 = nil
-
end
-
7
if r4
-
r0 = r4
-
else
-
7
if has_terminal?("May", false, index)
-
1
r5 = instantiate_node(SyntaxNode,input, index...(index + 3))
-
1
@index += 3
-
else
-
6
terminal_parse_failure("May")
-
6
r5 = nil
-
end
-
7
if r5
-
1
r0 = r5
-
else
-
6
if has_terminal?("Jun", false, index)
-
r6 = instantiate_node(SyntaxNode,input, index...(index + 3))
-
@index += 3
-
else
-
6
terminal_parse_failure("Jun")
-
6
r6 = nil
-
end
-
6
if r6
-
r0 = r6
-
else
-
6
if has_terminal?("Jul", false, index)
-
r7 = instantiate_node(SyntaxNode,input, index...(index + 3))
-
@index += 3
-
else
-
6
terminal_parse_failure("Jul")
-
6
r7 = nil
-
end
-
6
if r7
-
r0 = r7
-
else
-
6
if has_terminal?("Aug", false, index)
-
r8 = instantiate_node(SyntaxNode,input, index...(index + 3))
-
@index += 3
-
else
-
6
terminal_parse_failure("Aug")
-
6
r8 = nil
-
end
-
6
if r8
-
r0 = r8
-
else
-
6
if has_terminal?("Sep", false, index)
-
r9 = instantiate_node(SyntaxNode,input, index...(index + 3))
-
@index += 3
-
else
-
6
terminal_parse_failure("Sep")
-
6
r9 = nil
-
end
-
6
if r9
-
r0 = r9
-
else
-
6
if has_terminal?("Oct", false, index)
-
r10 = instantiate_node(SyntaxNode,input, index...(index + 3))
-
@index += 3
-
else
-
6
terminal_parse_failure("Oct")
-
6
r10 = nil
-
end
-
6
if r10
-
r0 = r10
-
else
-
6
if has_terminal?("Nov", false, index)
-
4
r11 = instantiate_node(SyntaxNode,input, index...(index + 3))
-
4
@index += 3
-
else
-
2
terminal_parse_failure("Nov")
-
2
r11 = nil
-
end
-
6
if r11
-
4
r0 = r11
-
else
-
2
if has_terminal?("Dec", false, index)
-
2
r12 = instantiate_node(SyntaxNode,input, index...(index + 3))
-
2
@index += 3
-
else
-
terminal_parse_failure("Dec")
-
r12 = nil
-
end
-
2
if r12
-
2
r0 = r12
-
else
-
@index = i0
-
r0 = nil
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
-
7
node_cache[:month_name][start_index] = r0
-
-
7
r0
-
end
-
-
1
module Day0
-
1
def DIGIT
-
elements[1]
-
end
-
-
end
-
-
1
def _nt_day
-
7
start_index = index
-
7
if node_cache[:day].has_key?(index)
-
cached = node_cache[:day][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
7
i0 = index
-
7
i1, s1 = index, []
-
7
r3 = _nt_FWS
-
7
if r3
-
7
r2 = r3
-
else
-
r2 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
7
s1 << r2
-
7
if r2
-
7
r4 = _nt_DIGIT
-
7
s1 << r4
-
7
if r4
-
7
r6 = _nt_DIGIT
-
7
if r6
-
7
r5 = r6
-
else
-
r5 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
7
s1 << r5
-
end
-
end
-
7
if s1.last
-
7
r1 = instantiate_node(SyntaxNode,input, i1...index, s1)
-
7
r1.extend(Day0)
-
else
-
@index = i1
-
r1 = nil
-
end
-
7
if r1
-
7
r0 = r1
-
else
-
r7 = _nt_obs_day
-
if r7
-
r0 = r7
-
else
-
@index = i0
-
r0 = nil
-
end
-
end
-
-
7
node_cache[:day][start_index] = r0
-
-
7
r0
-
end
-
-
1
module Time0
-
1
def time_of_day
-
elements[0]
-
end
-
-
1
def FWS
-
elements[1]
-
end
-
-
1
def zone
-
elements[2]
-
end
-
end
-
-
1
def _nt_time
-
7
start_index = index
-
7
if node_cache[:time].has_key?(index)
-
cached = node_cache[:time][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
7
i0, s0 = index, []
-
7
r1 = _nt_time_of_day
-
7
s0 << r1
-
7
if r1
-
7
r2 = _nt_FWS
-
7
s0 << r2
-
7
if r2
-
7
r3 = _nt_zone
-
7
s0 << r3
-
end
-
end
-
7
if s0.last
-
7
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
7
r0.extend(Time0)
-
else
-
@index = i0
-
r0 = nil
-
end
-
-
7
node_cache[:time][start_index] = r0
-
-
7
r0
-
end
-
-
1
module TimeOfDay0
-
1
def second
-
elements[1]
-
end
-
end
-
-
1
module TimeOfDay1
-
1
def hour
-
elements[0]
-
end
-
-
1
def minute
-
elements[2]
-
end
-
-
end
-
-
1
def _nt_time_of_day
-
7
start_index = index
-
7
if node_cache[:time_of_day].has_key?(index)
-
cached = node_cache[:time_of_day][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
7
i0, s0 = index, []
-
7
r1 = _nt_hour
-
7
s0 << r1
-
7
if r1
-
7
if has_terminal?(":", false, index)
-
7
r2 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
7
@index += 1
-
else
-
terminal_parse_failure(":")
-
r2 = nil
-
end
-
7
s0 << r2
-
7
if r2
-
7
r3 = _nt_minute
-
7
s0 << r3
-
7
if r3
-
7
i5, s5 = index, []
-
7
if has_terminal?(":", false, index)
-
7
r6 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
7
@index += 1
-
else
-
terminal_parse_failure(":")
-
r6 = nil
-
end
-
7
s5 << r6
-
7
if r6
-
7
r7 = _nt_second
-
7
s5 << r7
-
end
-
7
if s5.last
-
7
r5 = instantiate_node(SyntaxNode,input, i5...index, s5)
-
7
r5.extend(TimeOfDay0)
-
else
-
@index = i5
-
r5 = nil
-
end
-
7
if r5
-
7
r4 = r5
-
else
-
r4 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
7
s0 << r4
-
end
-
end
-
end
-
7
if s0.last
-
7
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
7
r0.extend(TimeOfDay1)
-
else
-
@index = i0
-
r0 = nil
-
end
-
-
7
node_cache[:time_of_day][start_index] = r0
-
-
7
r0
-
end
-
-
1
module Hour0
-
1
def DIGIT1
-
elements[0]
-
end
-
-
1
def DIGIT2
-
elements[1]
-
end
-
end
-
-
1
def _nt_hour
-
7
start_index = index
-
7
if node_cache[:hour].has_key?(index)
-
cached = node_cache[:hour][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
7
i0 = index
-
7
i1, s1 = index, []
-
7
r2 = _nt_DIGIT
-
7
s1 << r2
-
7
if r2
-
7
r3 = _nt_DIGIT
-
7
s1 << r3
-
end
-
7
if s1.last
-
7
r1 = instantiate_node(SyntaxNode,input, i1...index, s1)
-
7
r1.extend(Hour0)
-
else
-
@index = i1
-
r1 = nil
-
end
-
7
if r1
-
7
r0 = r1
-
else
-
r4 = _nt_obs_hour
-
if r4
-
r0 = r4
-
else
-
@index = i0
-
r0 = nil
-
end
-
end
-
-
7
node_cache[:hour][start_index] = r0
-
-
7
r0
-
end
-
-
1
module Minute0
-
1
def DIGIT1
-
elements[0]
-
end
-
-
1
def DIGIT2
-
elements[1]
-
end
-
end
-
-
1
def _nt_minute
-
7
start_index = index
-
7
if node_cache[:minute].has_key?(index)
-
cached = node_cache[:minute][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
7
i0 = index
-
7
i1, s1 = index, []
-
7
r2 = _nt_DIGIT
-
7
s1 << r2
-
7
if r2
-
7
r3 = _nt_DIGIT
-
7
s1 << r3
-
end
-
7
if s1.last
-
7
r1 = instantiate_node(SyntaxNode,input, i1...index, s1)
-
7
r1.extend(Minute0)
-
else
-
@index = i1
-
r1 = nil
-
end
-
7
if r1
-
7
r0 = r1
-
else
-
r4 = _nt_obs_minute
-
if r4
-
r0 = r4
-
else
-
@index = i0
-
r0 = nil
-
end
-
end
-
-
7
node_cache[:minute][start_index] = r0
-
-
7
r0
-
end
-
-
1
module Second0
-
1
def DIGIT1
-
elements[0]
-
end
-
-
1
def DIGIT2
-
elements[1]
-
end
-
end
-
-
1
def _nt_second
-
7
start_index = index
-
7
if node_cache[:second].has_key?(index)
-
cached = node_cache[:second][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
7
i0 = index
-
7
i1, s1 = index, []
-
7
r2 = _nt_DIGIT
-
7
s1 << r2
-
7
if r2
-
7
r3 = _nt_DIGIT
-
7
s1 << r3
-
end
-
7
if s1.last
-
7
r1 = instantiate_node(SyntaxNode,input, i1...index, s1)
-
7
r1.extend(Second0)
-
else
-
@index = i1
-
r1 = nil
-
end
-
7
if r1
-
7
r0 = r1
-
else
-
r4 = _nt_obs_second
-
if r4
-
r0 = r4
-
else
-
@index = i0
-
r0 = nil
-
end
-
end
-
-
7
node_cache[:second][start_index] = r0
-
-
7
r0
-
end
-
-
1
module Zone0
-
1
def DIGIT1
-
elements[1]
-
end
-
-
1
def DIGIT2
-
elements[2]
-
end
-
-
1
def DIGIT3
-
elements[3]
-
end
-
-
1
def DIGIT4
-
elements[4]
-
end
-
end
-
-
1
def _nt_zone
-
7
start_index = index
-
7
if node_cache[:zone].has_key?(index)
-
cached = node_cache[:zone][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
7
i0 = index
-
7
i1, s1 = index, []
-
7
i2 = index
-
7
if has_terminal?("+", false, index)
-
6
r3 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
6
@index += 1
-
else
-
1
terminal_parse_failure("+")
-
1
r3 = nil
-
end
-
7
if r3
-
6
r2 = r3
-
else
-
1
if has_terminal?("-", false, index)
-
1
r4 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
1
@index += 1
-
else
-
terminal_parse_failure("-")
-
r4 = nil
-
end
-
1
if r4
-
1
r2 = r4
-
else
-
@index = i2
-
r2 = nil
-
end
-
end
-
7
s1 << r2
-
7
if r2
-
7
r5 = _nt_DIGIT
-
7
s1 << r5
-
7
if r5
-
7
r6 = _nt_DIGIT
-
7
s1 << r6
-
7
if r6
-
7
r7 = _nt_DIGIT
-
7
s1 << r7
-
7
if r7
-
7
r8 = _nt_DIGIT
-
7
s1 << r8
-
end
-
end
-
end
-
end
-
7
if s1.last
-
7
r1 = instantiate_node(SyntaxNode,input, i1...index, s1)
-
7
r1.extend(Zone0)
-
else
-
@index = i1
-
r1 = nil
-
end
-
7
if r1
-
7
r0 = r1
-
else
-
r9 = _nt_obs_zone
-
if r9
-
r0 = r9
-
else
-
@index = i0
-
r0 = nil
-
end
-
end
-
-
7
node_cache[:zone][start_index] = r0
-
-
7
r0
-
end
-
-
1
module Return0
-
1
def path
-
elements[0]
-
end
-
-
1
def CRLF
-
elements[1]
-
end
-
end
-
-
1
def _nt_return
-
start_index = index
-
if node_cache[:return].has_key?(index)
-
cached = node_cache[:return][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
i0, s0 = index, []
-
r1 = _nt_path
-
s0 << r1
-
if r1
-
r2 = _nt_CRLF
-
s0 << r2
-
end
-
if s0.last
-
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
r0.extend(Return0)
-
else
-
@index = i0
-
r0 = nil
-
end
-
-
node_cache[:return][start_index] = r0
-
-
r0
-
end
-
-
1
module Path0
-
end
-
-
1
def _nt_path
-
start_index = index
-
if node_cache[:path].has_key?(index)
-
cached = node_cache[:path][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
i0 = index
-
i1, s1 = index, []
-
r3 = _nt_CFWS
-
if r3
-
r2 = r3
-
else
-
r2 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
s1 << r2
-
if r2
-
if has_terminal?("<", false, index)
-
r4 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure("<")
-
r4 = nil
-
end
-
s1 << r4
-
if r4
-
i5 = index
-
r7 = _nt_CFWS
-
if r7
-
r6 = r7
-
else
-
r6 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
if r6
-
r5 = r6
-
else
-
r8 = _nt_addr_spec
-
if r8
-
r5 = r8
-
else
-
@index = i5
-
r5 = nil
-
end
-
end
-
s1 << r5
-
if r5
-
if has_terminal?(">", false, index)
-
r9 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure(">")
-
r9 = nil
-
end
-
s1 << r9
-
if r9
-
r11 = _nt_CFWS
-
if r11
-
r10 = r11
-
else
-
r10 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
s1 << r10
-
end
-
end
-
end
-
end
-
if s1.last
-
r1 = instantiate_node(SyntaxNode,input, i1...index, s1)
-
r1.extend(Path0)
-
else
-
@index = i1
-
r1 = nil
-
end
-
if r1
-
r0 = r1
-
else
-
r12 = _nt_obs_path
-
if r12
-
r0 = r12
-
else
-
@index = i0
-
r0 = nil
-
end
-
end
-
-
node_cache[:path][start_index] = r0
-
-
r0
-
end
-
-
1
module Received0
-
1
def name_val_list
-
elements[0]
-
end
-
-
1
def date_time
-
elements[2]
-
end
-
-
1
def CRLF
-
elements[3]
-
end
-
end
-
-
1
def _nt_received
-
start_index = index
-
if node_cache[:received].has_key?(index)
-
cached = node_cache[:received][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
i0, s0 = index, []
-
r1 = _nt_name_val_list
-
s0 << r1
-
if r1
-
if has_terminal?(";", false, index)
-
r2 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure(";")
-
r2 = nil
-
end
-
s0 << r2
-
if r2
-
r3 = _nt_date_time
-
s0 << r3
-
if r3
-
r4 = _nt_CRLF
-
s0 << r4
-
end
-
end
-
end
-
if s0.last
-
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
r0.extend(Received0)
-
else
-
@index = i0
-
r0 = nil
-
end
-
-
node_cache[:received][start_index] = r0
-
-
r0
-
end
-
-
1
module NameValList0
-
1
def CFWS
-
elements[0]
-
end
-
-
1
def name_val_pair
-
elements[1]
-
end
-
end
-
-
1
module NameValList1
-
1
def name_val_pair
-
elements[0]
-
end
-
-
end
-
-
1
module NameValList2
-
end
-
-
1
def _nt_name_val_list
-
start_index = index
-
if node_cache[:name_val_list].has_key?(index)
-
cached = node_cache[:name_val_list][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
i0, s0 = index, []
-
r2 = _nt_CFWS
-
if r2
-
r1 = r2
-
else
-
r1 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
s0 << r1
-
if r1
-
i3, s3 = index, []
-
r4 = _nt_name_val_pair
-
s3 << r4
-
if r4
-
s5, i5 = [], index
-
loop do
-
i6, s6 = index, []
-
r7 = _nt_CFWS
-
s6 << r7
-
if r7
-
r8 = _nt_name_val_pair
-
s6 << r8
-
end
-
if s6.last
-
r6 = instantiate_node(SyntaxNode,input, i6...index, s6)
-
r6.extend(NameValList0)
-
else
-
@index = i6
-
r6 = nil
-
end
-
if r6
-
s5 << r6
-
else
-
break
-
end
-
end
-
r5 = instantiate_node(SyntaxNode,input, i5...index, s5)
-
s3 << r5
-
end
-
if s3.last
-
r3 = instantiate_node(SyntaxNode,input, i3...index, s3)
-
r3.extend(NameValList1)
-
else
-
@index = i3
-
r3 = nil
-
end
-
s0 << r3
-
end
-
if s0.last
-
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
r0.extend(NameValList2)
-
else
-
@index = i0
-
r0 = nil
-
end
-
-
node_cache[:name_val_list][start_index] = r0
-
-
r0
-
end
-
-
1
module NameValPair0
-
1
def item_name
-
elements[0]
-
end
-
-
1
def CFWS
-
elements[1]
-
end
-
-
1
def item_value
-
elements[2]
-
end
-
end
-
-
1
def _nt_name_val_pair
-
start_index = index
-
if node_cache[:name_val_pair].has_key?(index)
-
cached = node_cache[:name_val_pair][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
i0, s0 = index, []
-
r1 = _nt_item_name
-
s0 << r1
-
if r1
-
r2 = _nt_CFWS
-
s0 << r2
-
if r2
-
r3 = _nt_item_value
-
s0 << r3
-
end
-
end
-
if s0.last
-
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
r0.extend(NameValPair0)
-
else
-
@index = i0
-
r0 = nil
-
end
-
-
node_cache[:name_val_pair][start_index] = r0
-
-
r0
-
end
-
-
1
module ItemName0
-
end
-
-
1
module ItemName1
-
1
def ALPHA
-
elements[0]
-
end
-
-
end
-
-
1
def _nt_item_name
-
start_index = index
-
if node_cache[:item_name].has_key?(index)
-
cached = node_cache[:item_name][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
i0, s0 = index, []
-
r1 = _nt_ALPHA
-
s0 << r1
-
if r1
-
s2, i2 = [], index
-
loop do
-
i3, s3 = index, []
-
if has_terminal?("-", false, index)
-
r5 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure("-")
-
r5 = nil
-
end
-
if r5
-
r4 = r5
-
else
-
r4 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
s3 << r4
-
if r4
-
i6 = index
-
r7 = _nt_ALPHA
-
if r7
-
r6 = r7
-
else
-
r8 = _nt_DIGIT
-
if r8
-
r6 = r8
-
else
-
@index = i6
-
r6 = nil
-
end
-
end
-
s3 << r6
-
end
-
if s3.last
-
r3 = instantiate_node(SyntaxNode,input, i3...index, s3)
-
r3.extend(ItemName0)
-
else
-
@index = i3
-
r3 = nil
-
end
-
if r3
-
s2 << r3
-
else
-
break
-
end
-
end
-
r2 = instantiate_node(SyntaxNode,input, i2...index, s2)
-
s0 << r2
-
end
-
if s0.last
-
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
r0.extend(ItemName1)
-
else
-
@index = i0
-
r0 = nil
-
end
-
-
node_cache[:item_name][start_index] = r0
-
-
r0
-
end
-
-
1
def _nt_item_value
-
start_index = index
-
if node_cache[:item_value].has_key?(index)
-
cached = node_cache[:item_value][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
i0 = index
-
s1, i1 = [], index
-
loop do
-
r2 = _nt_angle_addr
-
if r2
-
s1 << r2
-
else
-
break
-
end
-
end
-
if s1.empty?
-
@index = i1
-
r1 = nil
-
else
-
r1 = instantiate_node(SyntaxNode,input, i1...index, s1)
-
end
-
if r1
-
r0 = r1
-
else
-
r3 = _nt_addr_spec
-
if r3
-
r0 = r3
-
else
-
r4 = _nt_atom
-
if r4
-
r0 = r4
-
else
-
r5 = _nt_domain
-
if r5
-
r0 = r5
-
else
-
r6 = _nt_msg_id
-
if r6
-
r0 = r6
-
else
-
@index = i0
-
r0 = nil
-
end
-
end
-
end
-
end
-
end
-
-
node_cache[:item_value][start_index] = r0
-
-
r0
-
end
-
-
1
module MessageIds0
-
1
def CFWS
-
elements[0]
-
end
-
-
1
def msg_id_value
-
elements[1]
-
end
-
end
-
-
1
module MessageIds1
-
1
def first_msg_id
-
68
elements[0]
-
end
-
-
1
def other_msg_ids
-
68
elements[1]
-
end
-
end
-
-
1
def _nt_message_ids
-
70
start_index = index
-
70
if node_cache[:message_ids].has_key?(index)
-
cached = node_cache[:message_ids][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
70
i0, s0 = index, []
-
70
r1 = _nt_msg_id
-
70
s0 << r1
-
70
if r1
-
68
s2, i2 = [], index
-
68
loop do
-
68
i3, s3 = index, []
-
68
r4 = _nt_CFWS
-
68
s3 << r4
-
68
if r4
-
68
r5 = _nt_msg_id
-
68
s3 << r5
-
end
-
68
if s3.last
-
r3 = instantiate_node(SyntaxNode,input, i3...index, s3)
-
r3.extend(MessageIds0)
-
else
-
68
@index = i3
-
68
r3 = nil
-
end
-
68
if r3
-
s2 << r3
-
else
-
68
break
-
end
-
end
-
68
r2 = instantiate_node(SyntaxNode,input, i2...index, s2)
-
68
s0 << r2
-
end
-
70
if s0.last
-
68
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
68
r0.extend(MessageIds1)
-
else
-
2
@index = i0
-
2
r0 = nil
-
end
-
-
70
node_cache[:message_ids][start_index] = r0
-
-
70
r0
-
end
-
-
1
module MsgId0
-
1
def msg_id_value
-
elements[2]
-
end
-
-
end
-
-
1
def _nt_msg_id
-
138
start_index = index
-
138
if node_cache[:msg_id].has_key?(index)
-
cached = node_cache[:msg_id][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
138
i0, s0 = index, []
-
138
r2 = _nt_CFWS
-
138
if r2
-
138
r1 = r2
-
else
-
r1 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
138
s0 << r1
-
138
if r1
-
138
if has_terminal?("<", false, index)
-
68
r3 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
68
@index += 1
-
else
-
70
terminal_parse_failure("<")
-
70
r3 = nil
-
end
-
138
s0 << r3
-
138
if r3
-
68
r4 = _nt_msg_id_value
-
68
s0 << r4
-
68
if r4
-
68
if has_terminal?(">", false, index)
-
68
r5 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
68
@index += 1
-
else
-
terminal_parse_failure(">")
-
r5 = nil
-
end
-
68
s0 << r5
-
68
if r5
-
68
r7 = _nt_CFWS
-
68
if r7
-
68
r6 = r7
-
else
-
r6 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
68
s0 << r6
-
end
-
end
-
end
-
end
-
138
if s0.last
-
68
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
68
r0.extend(MsgId0)
-
else
-
70
@index = i0
-
70
r0 = nil
-
end
-
-
138
node_cache[:msg_id][start_index] = r0
-
-
138
r0
-
end
-
-
1
module MsgIdValue0
-
1
def id_left
-
elements[0]
-
end
-
-
1
def id_right
-
elements[2]
-
end
-
end
-
-
1
def _nt_msg_id_value
-
68
start_index = index
-
68
if node_cache[:msg_id_value].has_key?(index)
-
cached = node_cache[:msg_id_value][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
68
i0, s0 = index, []
-
68
r1 = _nt_id_left
-
68
s0 << r1
-
68
if r1
-
68
if has_terminal?("@", false, index)
-
68
r2 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
68
@index += 1
-
else
-
terminal_parse_failure("@")
-
r2 = nil
-
end
-
68
s0 << r2
-
68
if r2
-
68
r3 = _nt_id_right
-
68
s0 << r3
-
end
-
end
-
68
if s0.last
-
68
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
68
r0.extend(MsgIdValue0)
-
else
-
@index = i0
-
r0 = nil
-
end
-
-
68
node_cache[:msg_id_value][start_index] = r0
-
-
68
r0
-
end
-
-
1
def _nt_id_left
-
68
start_index = index
-
68
if node_cache[:id_left].has_key?(index)
-
cached = node_cache[:id_left][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
68
i0 = index
-
68
r1 = _nt_message_id_text
-
68
if r1
-
68
r0 = r1
-
else
-
r2 = _nt_no_fold_quote
-
if r2
-
r0 = r2
-
else
-
r3 = _nt_obs_id_left
-
if r3
-
r0 = r3
-
else
-
@index = i0
-
r0 = nil
-
end
-
end
-
end
-
-
68
node_cache[:id_left][start_index] = r0
-
-
68
r0
-
end
-
-
1
def _nt_id_right
-
68
start_index = index
-
68
if node_cache[:id_right].has_key?(index)
-
cached = node_cache[:id_right][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
68
i0 = index
-
68
r1 = _nt_msg_id_dot_atom_text
-
68
if r1
-
68
r0 = r1
-
else
-
r2 = _nt_no_fold_literal
-
if r2
-
r0 = r2
-
else
-
r3 = _nt_obs_id_right
-
if r3
-
r0 = r3
-
else
-
@index = i0
-
r0 = nil
-
end
-
end
-
end
-
-
68
node_cache[:id_right][start_index] = r0
-
-
68
r0
-
end
-
-
1
module MsgIdDotAtomText0
-
1
def msg_id_domain_text
-
elements[0]
-
end
-
-
end
-
-
1
def _nt_msg_id_dot_atom_text
-
68
start_index = index
-
68
if node_cache[:msg_id_dot_atom_text].has_key?(index)
-
cached = node_cache[:msg_id_dot_atom_text][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
68
s0, i0 = [], index
-
68
loop do
-
195
i1, s1 = index, []
-
195
r2 = _nt_msg_id_domain_text
-
195
s1 << r2
-
195
if r2
-
127
if has_terminal?(".", false, index)
-
59
r4 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
59
@index += 1
-
else
-
68
terminal_parse_failure(".")
-
68
r4 = nil
-
end
-
127
if r4
-
59
r3 = r4
-
else
-
68
r3 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
127
s1 << r3
-
end
-
195
if s1.last
-
127
r1 = instantiate_node(SyntaxNode,input, i1...index, s1)
-
127
r1.extend(MsgIdDotAtomText0)
-
else
-
68
@index = i1
-
68
r1 = nil
-
end
-
195
if r1
-
127
s0 << r1
-
else
-
68
break
-
end
-
end
-
68
if s0.empty?
-
@index = i0
-
r0 = nil
-
else
-
68
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
end
-
-
68
node_cache[:msg_id_dot_atom_text][start_index] = r0
-
-
68
r0
-
end
-
-
1
module MsgIdDomainText0
-
1
def quoted_domain
-
elements[1]
-
end
-
end
-
-
1
module MsgIdDomainText1
-
1
def DQUOTE1
-
elements[0]
-
end
-
-
1
def DQUOTE2
-
elements[3]
-
end
-
end
-
-
1
def _nt_msg_id_domain_text
-
195
start_index = index
-
195
if node_cache[:msg_id_domain_text].has_key?(index)
-
cached = node_cache[:msg_id_domain_text][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
195
i0 = index
-
195
i1, s1 = index, []
-
195
r2 = _nt_DQUOTE
-
195
s1 << r2
-
195
if r2
-
s3, i3 = [], index
-
loop do
-
i4, s4 = index, []
-
r6 = _nt_FWS
-
if r6
-
r5 = r6
-
else
-
r5 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
s4 << r5
-
if r5
-
r7 = _nt_quoted_domain
-
s4 << r7
-
end
-
if s4.last
-
r4 = instantiate_node(SyntaxNode,input, i4...index, s4)
-
r4.extend(MsgIdDomainText0)
-
else
-
@index = i4
-
r4 = nil
-
end
-
if r4
-
s3 << r4
-
else
-
break
-
end
-
end
-
if s3.empty?
-
@index = i3
-
r3 = nil
-
else
-
r3 = instantiate_node(SyntaxNode,input, i3...index, s3)
-
end
-
s1 << r3
-
if r3
-
r9 = _nt_FWS
-
if r9
-
r8 = r9
-
else
-
r8 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
s1 << r8
-
if r8
-
r10 = _nt_DQUOTE
-
s1 << r10
-
end
-
end
-
end
-
195
if s1.last
-
r1 = instantiate_node(SyntaxNode,input, i1...index, s1)
-
r1.extend(MsgIdDomainText1)
-
else
-
195
@index = i1
-
195
r1 = nil
-
end
-
195
if r1
-
r0 = r1
-
else
-
195
s11, i11 = [], index
-
195
loop do
-
1226
r12 = _nt_msg_id_atext
-
1226
if r12
-
1031
s11 << r12
-
else
-
195
break
-
end
-
end
-
195
if s11.empty?
-
68
@index = i11
-
68
r11 = nil
-
else
-
127
r11 = instantiate_node(SyntaxNode,input, i11...index, s11)
-
end
-
195
if r11
-
127
r0 = r11
-
else
-
68
@index = i0
-
68
r0 = nil
-
end
-
end
-
-
195
node_cache[:msg_id_domain_text][start_index] = r0
-
-
195
r0
-
end
-
-
1
def _nt_msg_id_atext
-
1226
start_index = index
-
1226
if node_cache[:msg_id_atext].has_key?(index)
-
68
cached = node_cache[:msg_id_atext][index]
-
68
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
68
return cached
-
end
-
-
1158
i0 = index
-
1158
r1 = _nt_ALPHA
-
1158
if r1
-
904
r0 = r1
-
else
-
254
r2 = _nt_DIGIT
-
254
if r2
-
11
r0 = r2
-
else
-
243
if has_terminal?("!", false, index)
-
r3 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
243
terminal_parse_failure("!")
-
243
r3 = nil
-
end
-
243
if r3
-
r0 = r3
-
else
-
243
if has_terminal?("#", false, index)
-
r4 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
243
terminal_parse_failure("#")
-
243
r4 = nil
-
end
-
243
if r4
-
r0 = r4
-
else
-
243
if has_terminal?("$", false, index)
-
r5 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
243
terminal_parse_failure("$")
-
243
r5 = nil
-
end
-
243
if r5
-
r0 = r5
-
else
-
243
if has_terminal?("%", false, index)
-
r6 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
243
terminal_parse_failure("%")
-
243
r6 = nil
-
end
-
243
if r6
-
r0 = r6
-
else
-
243
if has_terminal?("&", false, index)
-
r7 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
243
terminal_parse_failure("&")
-
243
r7 = nil
-
end
-
243
if r7
-
r0 = r7
-
else
-
243
if has_terminal?("'", false, index)
-
r8 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
243
terminal_parse_failure("'")
-
243
r8 = nil
-
end
-
243
if r8
-
r0 = r8
-
else
-
243
if has_terminal?("*", false, index)
-
r9 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
243
terminal_parse_failure("*")
-
243
r9 = nil
-
end
-
243
if r9
-
r0 = r9
-
else
-
243
if has_terminal?("+", false, index)
-
r10 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
243
terminal_parse_failure("+")
-
243
r10 = nil
-
end
-
243
if r10
-
r0 = r10
-
else
-
243
if has_terminal?("-", false, index)
-
116
r11 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
116
@index += 1
-
else
-
127
terminal_parse_failure("-")
-
127
r11 = nil
-
end
-
243
if r11
-
116
r0 = r11
-
else
-
127
if has_terminal?("/", false, index)
-
r12 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
127
terminal_parse_failure("/")
-
127
r12 = nil
-
end
-
127
if r12
-
r0 = r12
-
else
-
127
if has_terminal?("=", false, index)
-
r13 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
127
terminal_parse_failure("=")
-
127
r13 = nil
-
end
-
127
if r13
-
r0 = r13
-
else
-
127
if has_terminal?("?", false, index)
-
r14 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
127
terminal_parse_failure("?")
-
127
r14 = nil
-
end
-
127
if r14
-
r0 = r14
-
else
-
127
if has_terminal?("^", false, index)
-
r15 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
127
terminal_parse_failure("^")
-
127
r15 = nil
-
end
-
127
if r15
-
r0 = r15
-
else
-
127
if has_terminal?("_", false, index)
-
r16 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
127
terminal_parse_failure("_")
-
127
r16 = nil
-
end
-
127
if r16
-
r0 = r16
-
else
-
127
if has_terminal?("`", false, index)
-
r17 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
127
terminal_parse_failure("`")
-
127
r17 = nil
-
end
-
127
if r17
-
r0 = r17
-
else
-
127
if has_terminal?("{", false, index)
-
r18 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
127
terminal_parse_failure("{")
-
127
r18 = nil
-
end
-
127
if r18
-
r0 = r18
-
else
-
127
if has_terminal?("|", false, index)
-
r19 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
127
terminal_parse_failure("|")
-
127
r19 = nil
-
end
-
127
if r19
-
r0 = r19
-
else
-
127
if has_terminal?("}", false, index)
-
r20 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
127
terminal_parse_failure("}")
-
127
r20 = nil
-
end
-
127
if r20
-
r0 = r20
-
else
-
127
if has_terminal?("~", false, index)
-
r21 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
127
terminal_parse_failure("~")
-
127
r21 = nil
-
end
-
127
if r21
-
r0 = r21
-
else
-
127
if has_terminal?("@", false, index)
-
r22 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
127
terminal_parse_failure("@")
-
127
r22 = nil
-
end
-
127
if r22
-
r0 = r22
-
else
-
127
@index = i0
-
127
r0 = nil
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
-
1158
node_cache[:msg_id_atext][start_index] = r0
-
-
1158
r0
-
end
-
-
1
module NoFoldQuote0
-
1
def DQUOTE1
-
elements[0]
-
end
-
-
1
def DQUOTE2
-
elements[2]
-
end
-
end
-
-
1
def _nt_no_fold_quote
-
start_index = index
-
if node_cache[:no_fold_quote].has_key?(index)
-
cached = node_cache[:no_fold_quote][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
i0, s0 = index, []
-
r1 = _nt_DQUOTE
-
s0 << r1
-
if r1
-
s2, i2 = [], index
-
loop do
-
i3 = index
-
r4 = _nt_qtext
-
if r4
-
r3 = r4
-
else
-
r5 = _nt_quoted_pair
-
if r5
-
r3 = r5
-
else
-
@index = i3
-
r3 = nil
-
end
-
end
-
if r3
-
s2 << r3
-
else
-
break
-
end
-
end
-
if s2.empty?
-
@index = i2
-
r2 = nil
-
else
-
r2 = instantiate_node(SyntaxNode,input, i2...index, s2)
-
end
-
s0 << r2
-
if r2
-
r6 = _nt_DQUOTE
-
s0 << r6
-
end
-
end
-
if s0.last
-
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
r0.extend(NoFoldQuote0)
-
else
-
@index = i0
-
r0 = nil
-
end
-
-
node_cache[:no_fold_quote][start_index] = r0
-
-
r0
-
end
-
-
1
module NoFoldLiteral0
-
end
-
-
1
def _nt_no_fold_literal
-
start_index = index
-
if node_cache[:no_fold_literal].has_key?(index)
-
cached = node_cache[:no_fold_literal][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
i0, s0 = index, []
-
if has_terminal?("[", false, index)
-
r1 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure("[")
-
r1 = nil
-
end
-
s0 << r1
-
if r1
-
s2, i2 = [], index
-
loop do
-
i3 = index
-
r4 = _nt_dtext
-
if r4
-
r3 = r4
-
else
-
r5 = _nt_quoted_pair
-
if r5
-
r3 = r5
-
else
-
@index = i3
-
r3 = nil
-
end
-
end
-
if r3
-
s2 << r3
-
else
-
break
-
end
-
end
-
if s2.empty?
-
@index = i2
-
r2 = nil
-
else
-
r2 = instantiate_node(SyntaxNode,input, i2...index, s2)
-
end
-
s0 << r2
-
if r2
-
if has_terminal?("]", false, index)
-
r6 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure("]")
-
r6 = nil
-
end
-
s0 << r6
-
end
-
end
-
if s0.last
-
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
r0.extend(NoFoldLiteral0)
-
else
-
@index = i0
-
r0 = nil
-
end
-
-
node_cache[:no_fold_literal][start_index] = r0
-
-
r0
-
end
-
-
end
-
-
1
class RFC2822Parser < Treetop::Runtime::CompiledParser
-
1
include RFC2822
-
end
-
-
end
-
# Autogenerated from a Treetop grammar. Edits may be lost.
-
-
-
1
module Mail
-
1
module RFC2822Obsolete
-
1
include Treetop::Runtime
-
-
1
def root
-
@root ||= :obs_qp
-
end
-
-
1
module ObsQp0
-
end
-
-
1
def _nt_obs_qp
-
52
start_index = index
-
52
if node_cache[:obs_qp].has_key?(index)
-
cached = node_cache[:obs_qp][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
52
i0, s0 = index, []
-
52
if has_terminal?("\\", false, index)
-
r1 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
52
terminal_parse_failure("\\")
-
52
r1 = nil
-
end
-
52
s0 << r1
-
52
if r1
-
if has_terminal?('\G[\\x00-\\x7F]', true, index)
-
r2 = true
-
@index += 1
-
else
-
r2 = nil
-
end
-
s0 << r2
-
end
-
52
if s0.last
-
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
r0.extend(ObsQp0)
-
else
-
52
@index = i0
-
52
r0 = nil
-
end
-
-
52
node_cache[:obs_qp][start_index] = r0
-
-
52
r0
-
end
-
-
1
module ObsText0
-
1
def obs_char
-
elements[0]
-
end
-
-
end
-
-
1
module ObsText1
-
end
-
-
1
def _nt_obs_text
-
start_index = index
-
if node_cache[:obs_text].has_key?(index)
-
cached = node_cache[:obs_text][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
i0, s0 = index, []
-
s1, i1 = [], index
-
loop do
-
r2 = _nt_LF
-
if r2
-
s1 << r2
-
else
-
break
-
end
-
end
-
r1 = instantiate_node(SyntaxNode,input, i1...index, s1)
-
s0 << r1
-
if r1
-
s3, i3 = [], index
-
loop do
-
r4 = _nt_CR
-
if r4
-
s3 << r4
-
else
-
break
-
end
-
end
-
r3 = instantiate_node(SyntaxNode,input, i3...index, s3)
-
s0 << r3
-
if r3
-
s5, i5 = [], index
-
loop do
-
i6, s6 = index, []
-
r7 = _nt_obs_char
-
s6 << r7
-
if r7
-
s8, i8 = [], index
-
loop do
-
r9 = _nt_LF
-
if r9
-
s8 << r9
-
else
-
break
-
end
-
end
-
r8 = instantiate_node(SyntaxNode,input, i8...index, s8)
-
s6 << r8
-
if r8
-
s10, i10 = [], index
-
loop do
-
r11 = _nt_CR
-
if r11
-
s10 << r11
-
else
-
break
-
end
-
end
-
r10 = instantiate_node(SyntaxNode,input, i10...index, s10)
-
s6 << r10
-
end
-
end
-
if s6.last
-
r6 = instantiate_node(SyntaxNode,input, i6...index, s6)
-
r6.extend(ObsText0)
-
else
-
@index = i6
-
r6 = nil
-
end
-
if r6
-
s5 << r6
-
else
-
break
-
end
-
end
-
r5 = instantiate_node(SyntaxNode,input, i5...index, s5)
-
s0 << r5
-
end
-
end
-
if s0.last
-
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
r0.extend(ObsText1)
-
else
-
@index = i0
-
r0 = nil
-
end
-
-
node_cache[:obs_text][start_index] = r0
-
-
r0
-
end
-
-
1
def _nt_obs_char
-
start_index = index
-
if node_cache[:obs_char].has_key?(index)
-
cached = node_cache[:obs_char][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
i0 = index
-
if has_terminal?('\G[\\x00-\\x09]', true, index)
-
r1 = true
-
@index += 1
-
else
-
r1 = nil
-
end
-
if r1
-
r0 = r1
-
else
-
if has_terminal?('\G[\\x0B-\\x0C]', true, index)
-
r2 = true
-
@index += 1
-
else
-
r2 = nil
-
end
-
if r2
-
r0 = r2
-
else
-
if has_terminal?('\G[\\x0E-\\x7F]', true, index)
-
r3 = true
-
@index += 1
-
else
-
r3 = nil
-
end
-
if r3
-
r0 = r3
-
else
-
@index = i0
-
r0 = nil
-
end
-
end
-
end
-
-
node_cache[:obs_char][start_index] = r0
-
-
r0
-
end
-
-
1
def _nt_obs_utext
-
start_index = index
-
if node_cache[:obs_utext].has_key?(index)
-
cached = node_cache[:obs_utext][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
r0 = _nt_obs_text
-
-
node_cache[:obs_utext][start_index] = r0
-
-
r0
-
end
-
-
1
def _nt_obs_phrase
-
289
start_index = index
-
289
if node_cache[:obs_phrase].has_key?(index)
-
cached = node_cache[:obs_phrase][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
289
s0, i0 = [], index
-
289
loop do
-
2139
i1 = index
-
2139
r2 = _nt_word
-
2139
if r2
-
1070
r1 = r2
-
else
-
1069
if has_terminal?(".", false, index)
-
492
r3 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
492
@index += 1
-
else
-
577
terminal_parse_failure(".")
-
577
r3 = nil
-
end
-
1069
if r3
-
492
r1 = r3
-
else
-
577
if has_terminal?("@", false, index)
-
288
r4 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
288
@index += 1
-
else
-
289
terminal_parse_failure("@")
-
289
r4 = nil
-
end
-
577
if r4
-
288
r1 = r4
-
else
-
289
@index = i1
-
289
r1 = nil
-
end
-
end
-
end
-
2139
if r1
-
1850
s0 << r1
-
else
-
289
break
-
end
-
end
-
289
if s0.empty?
-
@index = i0
-
r0 = nil
-
else
-
289
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
end
-
-
289
node_cache[:obs_phrase][start_index] = r0
-
-
289
r0
-
end
-
-
1
module ObsPhraseList0
-
end
-
-
1
module ObsPhraseList1
-
end
-
-
1
def _nt_obs_phrase_list
-
start_index = index
-
if node_cache[:obs_phrase_list].has_key?(index)
-
cached = node_cache[:obs_phrase_list][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
i0 = index
-
r1 = _nt_phrase
-
if r1
-
r0 = r1
-
else
-
i2, s2 = index, []
-
s3, i3 = [], index
-
loop do
-
i4, s4 = index, []
-
r6 = _nt_phrase
-
if r6
-
r5 = r6
-
else
-
r5 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
s4 << r5
-
if r5
-
r8 = _nt_CFWS
-
if r8
-
r7 = r8
-
else
-
r7 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
s4 << r7
-
if r7
-
if has_terminal?(",", false, index)
-
r9 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure(",")
-
r9 = nil
-
end
-
s4 << r9
-
if r9
-
r11 = _nt_CFWS
-
if r11
-
r10 = r11
-
else
-
r10 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
s4 << r10
-
end
-
end
-
end
-
if s4.last
-
r4 = instantiate_node(SyntaxNode,input, i4...index, s4)
-
r4.extend(ObsPhraseList0)
-
else
-
@index = i4
-
r4 = nil
-
end
-
if r4
-
s3 << r4
-
else
-
break
-
end
-
end
-
if s3.empty?
-
@index = i3
-
r3 = nil
-
else
-
r3 = instantiate_node(SyntaxNode,input, i3...index, s3)
-
end
-
s2 << r3
-
if r3
-
r13 = _nt_phrase
-
if r13
-
r12 = r13
-
else
-
r12 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
s2 << r12
-
end
-
if s2.last
-
r2 = instantiate_node(SyntaxNode,input, i2...index, s2)
-
r2.extend(ObsPhraseList1)
-
else
-
@index = i2
-
r2 = nil
-
end
-
if r2
-
r0 = r2
-
else
-
@index = i0
-
r0 = nil
-
end
-
end
-
-
node_cache[:obs_phrase_list][start_index] = r0
-
-
r0
-
end
-
-
1
module ObsFWS0
-
1
def CRLF
-
elements[0]
-
end
-
-
end
-
-
1
module ObsFWS1
-
end
-
-
1
def _nt_obs_FWS
-
5276
start_index = index
-
5276
if node_cache[:obs_FWS].has_key?(index)
-
cached = node_cache[:obs_FWS][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
5276
i0, s0 = index, []
-
5276
s1, i1 = [], index
-
5276
loop do
-
5443
r2 = _nt_WSP
-
5443
if r2
-
167
s1 << r2
-
else
-
5276
break
-
end
-
end
-
5276
if s1.empty?
-
5109
@index = i1
-
5109
r1 = nil
-
else
-
167
r1 = instantiate_node(SyntaxNode,input, i1...index, s1)
-
end
-
5276
s0 << r1
-
5276
if r1
-
167
s3, i3 = [], index
-
167
loop do
-
167
i4, s4 = index, []
-
167
r5 = _nt_CRLF
-
167
s4 << r5
-
167
if r5
-
s6, i6 = [], index
-
loop do
-
r7 = _nt_WSP
-
if r7
-
s6 << r7
-
else
-
break
-
end
-
end
-
if s6.empty?
-
@index = i6
-
r6 = nil
-
else
-
r6 = instantiate_node(SyntaxNode,input, i6...index, s6)
-
end
-
s4 << r6
-
end
-
167
if s4.last
-
r4 = instantiate_node(SyntaxNode,input, i4...index, s4)
-
r4.extend(ObsFWS0)
-
else
-
167
@index = i4
-
167
r4 = nil
-
end
-
167
if r4
-
s3 << r4
-
else
-
167
break
-
end
-
end
-
167
r3 = instantiate_node(SyntaxNode,input, i3...index, s3)
-
167
s0 << r3
-
end
-
5276
if s0.last
-
167
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
167
r0.extend(ObsFWS1)
-
else
-
5109
@index = i0
-
5109
r0 = nil
-
end
-
-
5276
node_cache[:obs_FWS][start_index] = r0
-
-
5276
r0
-
end
-
-
1
module ObsDayOfWeek0
-
1
def day_name
-
elements[1]
-
end
-
-
end
-
-
1
def _nt_obs_day_of_week
-
start_index = index
-
if node_cache[:obs_day_of_week].has_key?(index)
-
cached = node_cache[:obs_day_of_week][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
i0, s0 = index, []
-
r2 = _nt_CFWS
-
if r2
-
r1 = r2
-
else
-
r1 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
s0 << r1
-
if r1
-
r3 = _nt_day_name
-
s0 << r3
-
if r3
-
r5 = _nt_CFWS
-
if r5
-
r4 = r5
-
else
-
r4 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
s0 << r4
-
end
-
end
-
if s0.last
-
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
r0.extend(ObsDayOfWeek0)
-
else
-
@index = i0
-
r0 = nil
-
end
-
-
node_cache[:obs_day_of_week][start_index] = r0
-
-
r0
-
end
-
-
1
module ObsYear0
-
1
def DIGIT1
-
elements[0]
-
end
-
-
1
def DIGIT2
-
elements[1]
-
end
-
end
-
-
1
module ObsYear1
-
end
-
-
1
def _nt_obs_year
-
start_index = index
-
if node_cache[:obs_year].has_key?(index)
-
cached = node_cache[:obs_year][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
i0, s0 = index, []
-
r2 = _nt_CFWS
-
if r2
-
r1 = r2
-
else
-
r1 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
s0 << r1
-
if r1
-
i3, s3 = index, []
-
r4 = _nt_DIGIT
-
s3 << r4
-
if r4
-
r5 = _nt_DIGIT
-
s3 << r5
-
end
-
if s3.last
-
r3 = instantiate_node(SyntaxNode,input, i3...index, s3)
-
r3.extend(ObsYear0)
-
else
-
@index = i3
-
r3 = nil
-
end
-
s0 << r3
-
if r3
-
r7 = _nt_CFWS
-
if r7
-
r6 = r7
-
else
-
r6 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
s0 << r6
-
end
-
end
-
if s0.last
-
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
r0.extend(ObsYear1)
-
else
-
@index = i0
-
r0 = nil
-
end
-
-
node_cache[:obs_year][start_index] = r0
-
-
r0
-
end
-
-
1
module ObsMonth0
-
1
def CFWS1
-
elements[0]
-
end
-
-
1
def month_name
-
elements[1]
-
end
-
-
1
def CFWS2
-
elements[2]
-
end
-
end
-
-
1
def _nt_obs_month
-
start_index = index
-
if node_cache[:obs_month].has_key?(index)
-
cached = node_cache[:obs_month][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
i0, s0 = index, []
-
r1 = _nt_CFWS
-
s0 << r1
-
if r1
-
r2 = _nt_month_name
-
s0 << r2
-
if r2
-
r3 = _nt_CFWS
-
s0 << r3
-
end
-
end
-
if s0.last
-
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
r0.extend(ObsMonth0)
-
else
-
@index = i0
-
r0 = nil
-
end
-
-
node_cache[:obs_month][start_index] = r0
-
-
r0
-
end
-
-
1
module ObsDay0
-
1
def DIGIT1
-
elements[0]
-
end
-
-
1
def DIGIT2
-
elements[1]
-
end
-
end
-
-
1
module ObsDay1
-
end
-
-
1
def _nt_obs_day
-
start_index = index
-
if node_cache[:obs_day].has_key?(index)
-
cached = node_cache[:obs_day][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
i0, s0 = index, []
-
r2 = _nt_CFWS
-
if r2
-
r1 = r2
-
else
-
r1 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
s0 << r1
-
if r1
-
i3 = index
-
r4 = _nt_DIGIT
-
if r4
-
r3 = r4
-
else
-
i5, s5 = index, []
-
r6 = _nt_DIGIT
-
s5 << r6
-
if r6
-
r7 = _nt_DIGIT
-
s5 << r7
-
end
-
if s5.last
-
r5 = instantiate_node(SyntaxNode,input, i5...index, s5)
-
r5.extend(ObsDay0)
-
else
-
@index = i5
-
r5 = nil
-
end
-
if r5
-
r3 = r5
-
else
-
@index = i3
-
r3 = nil
-
end
-
end
-
s0 << r3
-
if r3
-
r9 = _nt_CFWS
-
if r9
-
r8 = r9
-
else
-
r8 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
s0 << r8
-
end
-
end
-
if s0.last
-
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
r0.extend(ObsDay1)
-
else
-
@index = i0
-
r0 = nil
-
end
-
-
node_cache[:obs_day][start_index] = r0
-
-
r0
-
end
-
-
1
module ObsHour0
-
1
def DIGIT1
-
elements[0]
-
end
-
-
1
def DIGIT2
-
elements[1]
-
end
-
end
-
-
1
module ObsHour1
-
end
-
-
1
def _nt_obs_hour
-
start_index = index
-
if node_cache[:obs_hour].has_key?(index)
-
cached = node_cache[:obs_hour][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
i0, s0 = index, []
-
r2 = _nt_CFWS
-
if r2
-
r1 = r2
-
else
-
r1 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
s0 << r1
-
if r1
-
i3, s3 = index, []
-
r4 = _nt_DIGIT
-
s3 << r4
-
if r4
-
r5 = _nt_DIGIT
-
s3 << r5
-
end
-
if s3.last
-
r3 = instantiate_node(SyntaxNode,input, i3...index, s3)
-
r3.extend(ObsHour0)
-
else
-
@index = i3
-
r3 = nil
-
end
-
s0 << r3
-
if r3
-
r7 = _nt_CFWS
-
if r7
-
r6 = r7
-
else
-
r6 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
s0 << r6
-
end
-
end
-
if s0.last
-
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
r0.extend(ObsHour1)
-
else
-
@index = i0
-
r0 = nil
-
end
-
-
node_cache[:obs_hour][start_index] = r0
-
-
r0
-
end
-
-
1
module ObsMinute0
-
1
def DIGIT1
-
elements[0]
-
end
-
-
1
def DIGIT2
-
elements[1]
-
end
-
end
-
-
1
module ObsMinute1
-
end
-
-
1
def _nt_obs_minute
-
start_index = index
-
if node_cache[:obs_minute].has_key?(index)
-
cached = node_cache[:obs_minute][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
i0, s0 = index, []
-
r2 = _nt_CFWS
-
if r2
-
r1 = r2
-
else
-
r1 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
s0 << r1
-
if r1
-
i3, s3 = index, []
-
r4 = _nt_DIGIT
-
s3 << r4
-
if r4
-
r5 = _nt_DIGIT
-
s3 << r5
-
end
-
if s3.last
-
r3 = instantiate_node(SyntaxNode,input, i3...index, s3)
-
r3.extend(ObsMinute0)
-
else
-
@index = i3
-
r3 = nil
-
end
-
s0 << r3
-
if r3
-
r7 = _nt_CFWS
-
if r7
-
r6 = r7
-
else
-
r6 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
s0 << r6
-
end
-
end
-
if s0.last
-
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
r0.extend(ObsMinute1)
-
else
-
@index = i0
-
r0 = nil
-
end
-
-
node_cache[:obs_minute][start_index] = r0
-
-
r0
-
end
-
-
1
module ObsSecond0
-
1
def DIGIT1
-
elements[0]
-
end
-
-
1
def DIGIT2
-
elements[1]
-
end
-
end
-
-
1
module ObsSecond1
-
end
-
-
1
def _nt_obs_second
-
start_index = index
-
if node_cache[:obs_second].has_key?(index)
-
cached = node_cache[:obs_second][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
i0, s0 = index, []
-
r2 = _nt_CFWS
-
if r2
-
r1 = r2
-
else
-
r1 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
s0 << r1
-
if r1
-
i3, s3 = index, []
-
r4 = _nt_DIGIT
-
s3 << r4
-
if r4
-
r5 = _nt_DIGIT
-
s3 << r5
-
end
-
if s3.last
-
r3 = instantiate_node(SyntaxNode,input, i3...index, s3)
-
r3.extend(ObsSecond0)
-
else
-
@index = i3
-
r3 = nil
-
end
-
s0 << r3
-
if r3
-
r7 = _nt_CFWS
-
if r7
-
r6 = r7
-
else
-
r6 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
s0 << r6
-
end
-
end
-
if s0.last
-
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
r0.extend(ObsSecond1)
-
else
-
@index = i0
-
r0 = nil
-
end
-
-
node_cache[:obs_second][start_index] = r0
-
-
r0
-
end
-
-
1
def _nt_obs_zone
-
start_index = index
-
if node_cache[:obs_zone].has_key?(index)
-
cached = node_cache[:obs_zone][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
i0 = index
-
if has_terminal?("UT", false, index)
-
r1 = instantiate_node(SyntaxNode,input, index...(index + 2))
-
@index += 2
-
else
-
terminal_parse_failure("UT")
-
r1 = nil
-
end
-
if r1
-
r0 = r1
-
else
-
if has_terminal?("GMT", false, index)
-
r2 = instantiate_node(SyntaxNode,input, index...(index + 3))
-
@index += 3
-
else
-
terminal_parse_failure("GMT")
-
r2 = nil
-
end
-
if r2
-
r0 = r2
-
else
-
if has_terminal?("EST", false, index)
-
r3 = instantiate_node(SyntaxNode,input, index...(index + 3))
-
@index += 3
-
else
-
terminal_parse_failure("EST")
-
r3 = nil
-
end
-
if r3
-
r0 = r3
-
else
-
if has_terminal?("EDT", false, index)
-
r4 = instantiate_node(SyntaxNode,input, index...(index + 3))
-
@index += 3
-
else
-
terminal_parse_failure("EDT")
-
r4 = nil
-
end
-
if r4
-
r0 = r4
-
else
-
if has_terminal?("CST", false, index)
-
r5 = instantiate_node(SyntaxNode,input, index...(index + 3))
-
@index += 3
-
else
-
terminal_parse_failure("CST")
-
r5 = nil
-
end
-
if r5
-
r0 = r5
-
else
-
if has_terminal?("CDT", false, index)
-
r6 = instantiate_node(SyntaxNode,input, index...(index + 3))
-
@index += 3
-
else
-
terminal_parse_failure("CDT")
-
r6 = nil
-
end
-
if r6
-
r0 = r6
-
else
-
if has_terminal?("MST", false, index)
-
r7 = instantiate_node(SyntaxNode,input, index...(index + 3))
-
@index += 3
-
else
-
terminal_parse_failure("MST")
-
r7 = nil
-
end
-
if r7
-
r0 = r7
-
else
-
if has_terminal?("MDT", false, index)
-
r8 = instantiate_node(SyntaxNode,input, index...(index + 3))
-
@index += 3
-
else
-
terminal_parse_failure("MDT")
-
r8 = nil
-
end
-
if r8
-
r0 = r8
-
else
-
if has_terminal?("PST", false, index)
-
r9 = instantiate_node(SyntaxNode,input, index...(index + 3))
-
@index += 3
-
else
-
terminal_parse_failure("PST")
-
r9 = nil
-
end
-
if r9
-
r0 = r9
-
else
-
if has_terminal?("PDT", false, index)
-
r10 = instantiate_node(SyntaxNode,input, index...(index + 3))
-
@index += 3
-
else
-
terminal_parse_failure("PDT")
-
r10 = nil
-
end
-
if r10
-
r0 = r10
-
else
-
if has_terminal?('\G[\\x41-\\x49]', true, index)
-
r11 = true
-
@index += 1
-
else
-
r11 = nil
-
end
-
if r11
-
r0 = r11
-
else
-
if has_terminal?('\G[\\x4B-\\x5A]', true, index)
-
r12 = true
-
@index += 1
-
else
-
r12 = nil
-
end
-
if r12
-
r0 = r12
-
else
-
if has_terminal?('\G[\\x61-\\x69]', true, index)
-
r13 = true
-
@index += 1
-
else
-
r13 = nil
-
end
-
if r13
-
r0 = r13
-
else
-
if has_terminal?('\G[\\x6B-\\x7A]', true, index)
-
r14 = true
-
@index += 1
-
else
-
r14 = nil
-
end
-
if r14
-
r0 = r14
-
else
-
@index = i0
-
r0 = nil
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
-
node_cache[:obs_zone][start_index] = r0
-
-
r0
-
end
-
-
1
module ObsAngleAddr0
-
1
def addr_spec
-
elements[3]
-
end
-
-
end
-
-
1
def _nt_obs_angle_addr
-
576
start_index = index
-
576
if node_cache[:obs_angle_addr].has_key?(index)
-
cached = node_cache[:obs_angle_addr][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
576
i0, s0 = index, []
-
576
r2 = _nt_CFWS
-
576
if r2
-
576
r1 = r2
-
else
-
r1 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
576
s0 << r1
-
576
if r1
-
576
if has_terminal?("<", false, index)
-
r3 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
576
terminal_parse_failure("<")
-
576
r3 = nil
-
end
-
576
s0 << r3
-
576
if r3
-
r5 = _nt_obs_route
-
if r5
-
r4 = r5
-
else
-
r4 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
s0 << r4
-
if r4
-
r6 = _nt_addr_spec
-
s0 << r6
-
if r6
-
if has_terminal?(">", false, index)
-
r7 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure(">")
-
r7 = nil
-
end
-
s0 << r7
-
if r7
-
r9 = _nt_CFWS
-
if r9
-
r8 = r9
-
else
-
r8 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
s0 << r8
-
end
-
end
-
end
-
end
-
end
-
576
if s0.last
-
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
r0.extend(ObsAngleAddr0)
-
else
-
576
@index = i0
-
576
r0 = nil
-
end
-
-
576
node_cache[:obs_angle_addr][start_index] = r0
-
-
576
r0
-
end
-
-
1
module ObsRoute0
-
1
def obs_domain_list
-
elements[1]
-
end
-
-
end
-
-
1
def _nt_obs_route
-
start_index = index
-
if node_cache[:obs_route].has_key?(index)
-
cached = node_cache[:obs_route][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
i0, s0 = index, []
-
r2 = _nt_CFWS
-
if r2
-
r1 = r2
-
else
-
r1 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
s0 << r1
-
if r1
-
r3 = _nt_obs_domain_list
-
s0 << r3
-
if r3
-
if has_terminal?(":", false, index)
-
r4 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure(":")
-
r4 = nil
-
end
-
s0 << r4
-
if r4
-
r6 = _nt_CFWS
-
if r6
-
r5 = r6
-
else
-
r5 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
s0 << r5
-
end
-
end
-
end
-
if s0.last
-
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
r0.extend(ObsRoute0)
-
else
-
@index = i0
-
r0 = nil
-
end
-
-
node_cache[:obs_route][start_index] = r0
-
-
r0
-
end
-
-
1
module ObsDomainList0
-
1
def domain
-
elements[3]
-
end
-
end
-
-
1
module ObsDomainList1
-
1
def domain
-
elements[1]
-
end
-
-
end
-
-
1
def _nt_obs_domain_list
-
start_index = index
-
if node_cache[:obs_domain_list].has_key?(index)
-
cached = node_cache[:obs_domain_list][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
i0, s0 = index, []
-
if has_terminal?("@", false, index)
-
r1 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure("@")
-
r1 = nil
-
end
-
s0 << r1
-
if r1
-
r2 = _nt_domain
-
s0 << r2
-
if r2
-
s3, i3 = [], index
-
loop do
-
i4, s4 = index, []
-
s5, i5 = [], index
-
loop do
-
if has_terminal?(",", false, index)
-
r6 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure(",")
-
r6 = nil
-
end
-
if r6
-
s5 << r6
-
else
-
break
-
end
-
end
-
r5 = instantiate_node(SyntaxNode,input, i5...index, s5)
-
s4 << r5
-
if r5
-
r8 = _nt_CFWS
-
if r8
-
r7 = r8
-
else
-
r7 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
s4 << r7
-
if r7
-
if has_terminal?("@", false, index)
-
r9 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure("@")
-
r9 = nil
-
end
-
s4 << r9
-
if r9
-
r10 = _nt_domain
-
s4 << r10
-
end
-
end
-
end
-
if s4.last
-
r4 = instantiate_node(SyntaxNode,input, i4...index, s4)
-
r4.extend(ObsDomainList0)
-
else
-
@index = i4
-
r4 = nil
-
end
-
if r4
-
s3 << r4
-
else
-
break
-
end
-
end
-
r3 = instantiate_node(SyntaxNode,input, i3...index, s3)
-
s0 << r3
-
end
-
end
-
if s0.last
-
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
r0.extend(ObsDomainList1)
-
else
-
@index = i0
-
r0 = nil
-
end
-
-
node_cache[:obs_domain_list][start_index] = r0
-
-
r0
-
end
-
-
1
module ObsLocalPart0
-
1
def word
-
elements[1]
-
end
-
end
-
-
1
module ObsLocalPart1
-
1
def word
-
elements[0]
-
end
-
-
end
-
-
1
def _nt_obs_local_part
-
start_index = index
-
if node_cache[:obs_local_part].has_key?(index)
-
cached = node_cache[:obs_local_part][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
i0, s0 = index, []
-
r1 = _nt_word
-
s0 << r1
-
if r1
-
s2, i2 = [], index
-
loop do
-
i3, s3 = index, []
-
if has_terminal?(".", false, index)
-
r4 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure(".")
-
r4 = nil
-
end
-
s3 << r4
-
if r4
-
r5 = _nt_word
-
s3 << r5
-
end
-
if s3.last
-
r3 = instantiate_node(SyntaxNode,input, i3...index, s3)
-
r3.extend(ObsLocalPart0)
-
else
-
@index = i3
-
r3 = nil
-
end
-
if r3
-
s2 << r3
-
else
-
break
-
end
-
end
-
r2 = instantiate_node(SyntaxNode,input, i2...index, s2)
-
s0 << r2
-
end
-
if s0.last
-
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
r0.extend(ObsLocalPart1)
-
else
-
@index = i0
-
r0 = nil
-
end
-
-
node_cache[:obs_local_part][start_index] = r0
-
-
r0
-
end
-
-
1
module ObsDomain0
-
1
def atom
-
elements[1]
-
end
-
end
-
-
1
module ObsDomain1
-
1
def atom
-
elements[0]
-
end
-
-
end
-
-
1
def _nt_obs_domain
-
start_index = index
-
if node_cache[:obs_domain].has_key?(index)
-
cached = node_cache[:obs_domain][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
i0, s0 = index, []
-
r1 = _nt_atom
-
s0 << r1
-
if r1
-
s2, i2 = [], index
-
loop do
-
i3, s3 = index, []
-
if has_terminal?(".", false, index)
-
r4 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure(".")
-
r4 = nil
-
end
-
s3 << r4
-
if r4
-
r5 = _nt_atom
-
s3 << r5
-
end
-
if s3.last
-
r3 = instantiate_node(SyntaxNode,input, i3...index, s3)
-
r3.extend(ObsDomain0)
-
else
-
@index = i3
-
r3 = nil
-
end
-
if r3
-
s2 << r3
-
else
-
break
-
end
-
end
-
r2 = instantiate_node(SyntaxNode,input, i2...index, s2)
-
s0 << r2
-
end
-
if s0.last
-
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
r0.extend(ObsDomain1)
-
else
-
@index = i0
-
r0 = nil
-
end
-
-
node_cache[:obs_domain][start_index] = r0
-
-
r0
-
end
-
-
1
module ObsMboxList0
-
end
-
-
1
module ObsMboxList1
-
end
-
-
1
def _nt_obs_mbox_list
-
start_index = index
-
if node_cache[:obs_mbox_list].has_key?(index)
-
cached = node_cache[:obs_mbox_list][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
i0, s0 = index, []
-
s1, i1 = [], index
-
loop do
-
i2, s2 = index, []
-
r4 = _nt_mailbox
-
if r4
-
r3 = r4
-
else
-
r3 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
s2 << r3
-
if r3
-
r6 = _nt_CFWS
-
if r6
-
r5 = r6
-
else
-
r5 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
s2 << r5
-
if r5
-
if has_terminal?(",", false, index)
-
r7 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure(",")
-
r7 = nil
-
end
-
s2 << r7
-
if r7
-
r9 = _nt_CFWS
-
if r9
-
r8 = r9
-
else
-
r8 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
s2 << r8
-
end
-
end
-
end
-
if s2.last
-
r2 = instantiate_node(SyntaxNode,input, i2...index, s2)
-
r2.extend(ObsMboxList0)
-
else
-
@index = i2
-
r2 = nil
-
end
-
if r2
-
s1 << r2
-
else
-
break
-
end
-
end
-
if s1.empty?
-
@index = i1
-
r1 = nil
-
else
-
r1 = instantiate_node(SyntaxNode,input, i1...index, s1)
-
end
-
s0 << r1
-
if r1
-
r11 = _nt_mailbox
-
if r11
-
r10 = r11
-
else
-
r10 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
s0 << r10
-
end
-
if s0.last
-
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
r0.extend(ObsMboxList1)
-
else
-
@index = i0
-
r0 = nil
-
end
-
-
node_cache[:obs_mbox_list][start_index] = r0
-
-
r0
-
end
-
-
1
module ObsAddrList0
-
end
-
-
1
module ObsAddrList1
-
end
-
-
1
def _nt_obs_addr_list
-
start_index = index
-
if node_cache[:obs_addr_list].has_key?(index)
-
cached = node_cache[:obs_addr_list][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
i0, s0 = index, []
-
s1, i1 = [], index
-
loop do
-
i2, s2 = index, []
-
r4 = _nt_address
-
if r4
-
r3 = r4
-
else
-
r3 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
s2 << r3
-
if r3
-
r6 = _nt_CFWS
-
if r6
-
r5 = r6
-
else
-
r5 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
s2 << r5
-
if r5
-
if has_terminal?(",", false, index)
-
r7 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure(",")
-
r7 = nil
-
end
-
s2 << r7
-
if r7
-
r9 = _nt_CFWS
-
if r9
-
r8 = r9
-
else
-
r8 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
s2 << r8
-
end
-
end
-
end
-
if s2.last
-
r2 = instantiate_node(SyntaxNode,input, i2...index, s2)
-
r2.extend(ObsAddrList0)
-
else
-
@index = i2
-
r2 = nil
-
end
-
if r2
-
s1 << r2
-
else
-
break
-
end
-
end
-
if s1.empty?
-
@index = i1
-
r1 = nil
-
else
-
r1 = instantiate_node(SyntaxNode,input, i1...index, s1)
-
end
-
s0 << r1
-
if r1
-
r11 = _nt_address
-
if r11
-
r10 = r11
-
else
-
r10 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
s0 << r10
-
end
-
if s0.last
-
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
r0.extend(ObsAddrList1)
-
else
-
@index = i0
-
r0 = nil
-
end
-
-
node_cache[:obs_addr_list][start_index] = r0
-
-
r0
-
end
-
-
1
def _nt_obs_fields
-
start_index = index
-
if node_cache[:obs_fields].has_key?(index)
-
cached = node_cache[:obs_fields][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
s0, i0 = [], index
-
loop do
-
i1 = index
-
r2 = _nt_obs_return
-
if r2
-
r1 = r2
-
else
-
r3 = _nt_obs_received
-
if r3
-
r1 = r3
-
else
-
r4 = _nt_obs_orig_date
-
if r4
-
r1 = r4
-
else
-
r5 = _nt_obs_from
-
if r5
-
r1 = r5
-
else
-
r6 = _nt_obs_sender
-
if r6
-
r1 = r6
-
else
-
r7 = _nt_obs_reply_to
-
if r7
-
r1 = r7
-
else
-
r8 = _nt_obs_to
-
if r8
-
r1 = r8
-
else
-
r9 = _nt_obs_cc
-
if r9
-
r1 = r9
-
else
-
r10 = _nt_obs_bcc
-
if r10
-
r1 = r10
-
else
-
r11 = _nt_obs_message_id
-
if r11
-
r1 = r11
-
else
-
r12 = _nt_obs_in_reply_to
-
if r12
-
r1 = r12
-
else
-
r13 = _nt_obs_references
-
if r13
-
r1 = r13
-
else
-
r14 = _nt_obs_subject
-
if r14
-
r1 = r14
-
else
-
r15 = _nt_obs_comments
-
if r15
-
r1 = r15
-
else
-
r16 = _nt_obs_keywords
-
if r16
-
r1 = r16
-
else
-
r17 = _nt_obs_resent_date
-
if r17
-
r1 = r17
-
else
-
r18 = _nt_obs_resent_from
-
if r18
-
r1 = r18
-
else
-
r19 = _nt_obs_resent_send
-
if r19
-
r1 = r19
-
else
-
r20 = _nt_obs_resent_rply
-
if r20
-
r1 = r20
-
else
-
r21 = _nt_obs_resent_to
-
if r21
-
r1 = r21
-
else
-
r22 = _nt_obs_resent_cc
-
if r22
-
r1 = r22
-
else
-
r23 = _nt_obs_resent_bcc
-
if r23
-
r1 = r23
-
else
-
r24 = _nt_obs_resent_mid
-
if r24
-
r1 = r24
-
else
-
r25 = _nt_obs_optional
-
if r25
-
r1 = r25
-
else
-
@index = i1
-
r1 = nil
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
if r1
-
s0 << r1
-
else
-
break
-
end
-
end
-
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
-
node_cache[:obs_fields][start_index] = r0
-
-
r0
-
end
-
-
1
module ObsOrigDate0
-
1
def date_time
-
elements[3]
-
end
-
-
1
def CRLF
-
elements[4]
-
end
-
end
-
-
1
def _nt_obs_orig_date
-
start_index = index
-
if node_cache[:obs_orig_date].has_key?(index)
-
cached = node_cache[:obs_orig_date][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
i0, s0 = index, []
-
if has_terminal?("Date", false, index)
-
r1 = instantiate_node(SyntaxNode,input, index...(index + 4))
-
@index += 4
-
else
-
terminal_parse_failure("Date")
-
r1 = nil
-
end
-
s0 << r1
-
if r1
-
s2, i2 = [], index
-
loop do
-
r3 = _nt_WSP
-
if r3
-
s2 << r3
-
else
-
break
-
end
-
end
-
r2 = instantiate_node(SyntaxNode,input, i2...index, s2)
-
s0 << r2
-
if r2
-
if has_terminal?(":", false, index)
-
r4 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure(":")
-
r4 = nil
-
end
-
s0 << r4
-
if r4
-
r5 = _nt_date_time
-
s0 << r5
-
if r5
-
r6 = _nt_CRLF
-
s0 << r6
-
end
-
end
-
end
-
end
-
if s0.last
-
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
r0.extend(ObsOrigDate0)
-
else
-
@index = i0
-
r0 = nil
-
end
-
-
node_cache[:obs_orig_date][start_index] = r0
-
-
r0
-
end
-
-
1
module ObsFrom0
-
1
def mailbox_list
-
elements[3]
-
end
-
-
1
def CRLF
-
elements[4]
-
end
-
end
-
-
1
def _nt_obs_from
-
start_index = index
-
if node_cache[:obs_from].has_key?(index)
-
cached = node_cache[:obs_from][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
i0, s0 = index, []
-
if has_terminal?("From", false, index)
-
r1 = instantiate_node(SyntaxNode,input, index...(index + 4))
-
@index += 4
-
else
-
terminal_parse_failure("From")
-
r1 = nil
-
end
-
s0 << r1
-
if r1
-
s2, i2 = [], index
-
loop do
-
r3 = _nt_WSP
-
if r3
-
s2 << r3
-
else
-
break
-
end
-
end
-
r2 = instantiate_node(SyntaxNode,input, i2...index, s2)
-
s0 << r2
-
if r2
-
if has_terminal?(":", false, index)
-
r4 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure(":")
-
r4 = nil
-
end
-
s0 << r4
-
if r4
-
r5 = _nt_mailbox_list
-
s0 << r5
-
if r5
-
r6 = _nt_CRLF
-
s0 << r6
-
end
-
end
-
end
-
end
-
if s0.last
-
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
r0.extend(ObsFrom0)
-
else
-
@index = i0
-
r0 = nil
-
end
-
-
node_cache[:obs_from][start_index] = r0
-
-
r0
-
end
-
-
1
module ObsSender0
-
1
def mailbox
-
elements[3]
-
end
-
-
1
def CRLF
-
elements[4]
-
end
-
end
-
-
1
def _nt_obs_sender
-
start_index = index
-
if node_cache[:obs_sender].has_key?(index)
-
cached = node_cache[:obs_sender][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
i0, s0 = index, []
-
if has_terminal?("Sender", false, index)
-
r1 = instantiate_node(SyntaxNode,input, index...(index + 6))
-
@index += 6
-
else
-
terminal_parse_failure("Sender")
-
r1 = nil
-
end
-
s0 << r1
-
if r1
-
s2, i2 = [], index
-
loop do
-
r3 = _nt_WSP
-
if r3
-
s2 << r3
-
else
-
break
-
end
-
end
-
r2 = instantiate_node(SyntaxNode,input, i2...index, s2)
-
s0 << r2
-
if r2
-
if has_terminal?(":", false, index)
-
r4 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure(":")
-
r4 = nil
-
end
-
s0 << r4
-
if r4
-
r5 = _nt_mailbox
-
s0 << r5
-
if r5
-
r6 = _nt_CRLF
-
s0 << r6
-
end
-
end
-
end
-
end
-
if s0.last
-
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
r0.extend(ObsSender0)
-
else
-
@index = i0
-
r0 = nil
-
end
-
-
node_cache[:obs_sender][start_index] = r0
-
-
r0
-
end
-
-
1
module ObsReplyTo0
-
1
def mailbox_list
-
elements[3]
-
end
-
-
1
def CRLF
-
elements[4]
-
end
-
end
-
-
1
def _nt_obs_reply_to
-
start_index = index
-
if node_cache[:obs_reply_to].has_key?(index)
-
cached = node_cache[:obs_reply_to][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
i0, s0 = index, []
-
if has_terminal?("Reply-To", false, index)
-
r1 = instantiate_node(SyntaxNode,input, index...(index + 8))
-
@index += 8
-
else
-
terminal_parse_failure("Reply-To")
-
r1 = nil
-
end
-
s0 << r1
-
if r1
-
s2, i2 = [], index
-
loop do
-
r3 = _nt_WSP
-
if r3
-
s2 << r3
-
else
-
break
-
end
-
end
-
r2 = instantiate_node(SyntaxNode,input, i2...index, s2)
-
s0 << r2
-
if r2
-
if has_terminal?(":", false, index)
-
r4 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure(":")
-
r4 = nil
-
end
-
s0 << r4
-
if r4
-
r5 = _nt_mailbox_list
-
s0 << r5
-
if r5
-
r6 = _nt_CRLF
-
s0 << r6
-
end
-
end
-
end
-
end
-
if s0.last
-
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
r0.extend(ObsReplyTo0)
-
else
-
@index = i0
-
r0 = nil
-
end
-
-
node_cache[:obs_reply_to][start_index] = r0
-
-
r0
-
end
-
-
1
module ObsTo0
-
1
def address_list
-
elements[3]
-
end
-
-
1
def CRLF
-
elements[4]
-
end
-
end
-
-
1
def _nt_obs_to
-
start_index = index
-
if node_cache[:obs_to].has_key?(index)
-
cached = node_cache[:obs_to][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
i0, s0 = index, []
-
if has_terminal?("To", false, index)
-
r1 = instantiate_node(SyntaxNode,input, index...(index + 2))
-
@index += 2
-
else
-
terminal_parse_failure("To")
-
r1 = nil
-
end
-
s0 << r1
-
if r1
-
s2, i2 = [], index
-
loop do
-
r3 = _nt_WSP
-
if r3
-
s2 << r3
-
else
-
break
-
end
-
end
-
r2 = instantiate_node(SyntaxNode,input, i2...index, s2)
-
s0 << r2
-
if r2
-
if has_terminal?(":", false, index)
-
r4 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure(":")
-
r4 = nil
-
end
-
s0 << r4
-
if r4
-
r5 = _nt_address_list
-
s0 << r5
-
if r5
-
r6 = _nt_CRLF
-
s0 << r6
-
end
-
end
-
end
-
end
-
if s0.last
-
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
r0.extend(ObsTo0)
-
else
-
@index = i0
-
r0 = nil
-
end
-
-
node_cache[:obs_to][start_index] = r0
-
-
r0
-
end
-
-
1
module ObsCc0
-
1
def address_list
-
elements[3]
-
end
-
-
1
def CRLF
-
elements[4]
-
end
-
end
-
-
1
def _nt_obs_cc
-
start_index = index
-
if node_cache[:obs_cc].has_key?(index)
-
cached = node_cache[:obs_cc][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
i0, s0 = index, []
-
if has_terminal?("Cc", false, index)
-
r1 = instantiate_node(SyntaxNode,input, index...(index + 2))
-
@index += 2
-
else
-
terminal_parse_failure("Cc")
-
r1 = nil
-
end
-
s0 << r1
-
if r1
-
s2, i2 = [], index
-
loop do
-
r3 = _nt_WSP
-
if r3
-
s2 << r3
-
else
-
break
-
end
-
end
-
r2 = instantiate_node(SyntaxNode,input, i2...index, s2)
-
s0 << r2
-
if r2
-
if has_terminal?(":", false, index)
-
r4 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure(":")
-
r4 = nil
-
end
-
s0 << r4
-
if r4
-
r5 = _nt_address_list
-
s0 << r5
-
if r5
-
r6 = _nt_CRLF
-
s0 << r6
-
end
-
end
-
end
-
end
-
if s0.last
-
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
r0.extend(ObsCc0)
-
else
-
@index = i0
-
r0 = nil
-
end
-
-
node_cache[:obs_cc][start_index] = r0
-
-
r0
-
end
-
-
1
module ObsBcc0
-
1
def CRLF
-
elements[4]
-
end
-
end
-
-
1
def _nt_obs_bcc
-
start_index = index
-
if node_cache[:obs_bcc].has_key?(index)
-
cached = node_cache[:obs_bcc][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
i0, s0 = index, []
-
if has_terminal?("Bcc", false, index)
-
r1 = instantiate_node(SyntaxNode,input, index...(index + 3))
-
@index += 3
-
else
-
terminal_parse_failure("Bcc")
-
r1 = nil
-
end
-
s0 << r1
-
if r1
-
s2, i2 = [], index
-
loop do
-
r3 = _nt_WSP
-
if r3
-
s2 << r3
-
else
-
break
-
end
-
end
-
r2 = instantiate_node(SyntaxNode,input, i2...index, s2)
-
s0 << r2
-
if r2
-
if has_terminal?(":", false, index)
-
r4 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure(":")
-
r4 = nil
-
end
-
s0 << r4
-
if r4
-
i5 = index
-
r6 = _nt_address_list
-
if r6
-
r5 = r6
-
else
-
r8 = _nt_CFWS
-
if r8
-
r7 = r8
-
else
-
r7 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
if r7
-
r5 = r7
-
else
-
@index = i5
-
r5 = nil
-
end
-
end
-
s0 << r5
-
if r5
-
r9 = _nt_CRLF
-
s0 << r9
-
end
-
end
-
end
-
end
-
if s0.last
-
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
r0.extend(ObsBcc0)
-
else
-
@index = i0
-
r0 = nil
-
end
-
-
node_cache[:obs_bcc][start_index] = r0
-
-
r0
-
end
-
-
1
module ObsMessageId0
-
1
def msg_id
-
elements[3]
-
end
-
-
1
def CRLF
-
elements[4]
-
end
-
end
-
-
1
def _nt_obs_message_id
-
start_index = index
-
if node_cache[:obs_message_id].has_key?(index)
-
cached = node_cache[:obs_message_id][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
i0, s0 = index, []
-
if has_terminal?("Message-ID", false, index)
-
r1 = instantiate_node(SyntaxNode,input, index...(index + 10))
-
@index += 10
-
else
-
terminal_parse_failure("Message-ID")
-
r1 = nil
-
end
-
s0 << r1
-
if r1
-
s2, i2 = [], index
-
loop do
-
r3 = _nt_WSP
-
if r3
-
s2 << r3
-
else
-
break
-
end
-
end
-
r2 = instantiate_node(SyntaxNode,input, i2...index, s2)
-
s0 << r2
-
if r2
-
if has_terminal?(":", false, index)
-
r4 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure(":")
-
r4 = nil
-
end
-
s0 << r4
-
if r4
-
r5 = _nt_msg_id
-
s0 << r5
-
if r5
-
r6 = _nt_CRLF
-
s0 << r6
-
end
-
end
-
end
-
end
-
if s0.last
-
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
r0.extend(ObsMessageId0)
-
else
-
@index = i0
-
r0 = nil
-
end
-
-
node_cache[:obs_message_id][start_index] = r0
-
-
r0
-
end
-
-
1
module ObsInReplyTo0
-
1
def CRLF
-
elements[4]
-
end
-
end
-
-
1
def _nt_obs_in_reply_to
-
start_index = index
-
if node_cache[:obs_in_reply_to].has_key?(index)
-
cached = node_cache[:obs_in_reply_to][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
i0, s0 = index, []
-
if has_terminal?("In-Reply-To", false, index)
-
r1 = instantiate_node(SyntaxNode,input, index...(index + 11))
-
@index += 11
-
else
-
terminal_parse_failure("In-Reply-To")
-
r1 = nil
-
end
-
s0 << r1
-
if r1
-
s2, i2 = [], index
-
loop do
-
r3 = _nt_WSP
-
if r3
-
s2 << r3
-
else
-
break
-
end
-
end
-
r2 = instantiate_node(SyntaxNode,input, i2...index, s2)
-
s0 << r2
-
if r2
-
if has_terminal?(":", false, index)
-
r4 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure(":")
-
r4 = nil
-
end
-
s0 << r4
-
if r4
-
s5, i5 = [], index
-
loop do
-
i6 = index
-
r7 = _nt_phrase
-
if r7
-
r6 = r7
-
else
-
r8 = _nt_msg_id
-
if r8
-
r6 = r8
-
else
-
@index = i6
-
r6 = nil
-
end
-
end
-
if r6
-
s5 << r6
-
else
-
break
-
end
-
end
-
r5 = instantiate_node(SyntaxNode,input, i5...index, s5)
-
s0 << r5
-
if r5
-
r9 = _nt_CRLF
-
s0 << r9
-
end
-
end
-
end
-
end
-
if s0.last
-
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
r0.extend(ObsInReplyTo0)
-
else
-
@index = i0
-
r0 = nil
-
end
-
-
node_cache[:obs_in_reply_to][start_index] = r0
-
-
r0
-
end
-
-
1
module ObsReferences0
-
1
def CRLF
-
elements[4]
-
end
-
end
-
-
1
def _nt_obs_references
-
start_index = index
-
if node_cache[:obs_references].has_key?(index)
-
cached = node_cache[:obs_references][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
i0, s0 = index, []
-
if has_terminal?("References", false, index)
-
r1 = instantiate_node(SyntaxNode,input, index...(index + 10))
-
@index += 10
-
else
-
terminal_parse_failure("References")
-
r1 = nil
-
end
-
s0 << r1
-
if r1
-
s2, i2 = [], index
-
loop do
-
r3 = _nt_WSP
-
if r3
-
s2 << r3
-
else
-
break
-
end
-
end
-
r2 = instantiate_node(SyntaxNode,input, i2...index, s2)
-
s0 << r2
-
if r2
-
if has_terminal?(":", false, index)
-
r4 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure(":")
-
r4 = nil
-
end
-
s0 << r4
-
if r4
-
s5, i5 = [], index
-
loop do
-
i6 = index
-
r7 = _nt_phrase
-
if r7
-
r6 = r7
-
else
-
r8 = _nt_msg_id
-
if r8
-
r6 = r8
-
else
-
@index = i6
-
r6 = nil
-
end
-
end
-
if r6
-
s5 << r6
-
else
-
break
-
end
-
end
-
r5 = instantiate_node(SyntaxNode,input, i5...index, s5)
-
s0 << r5
-
if r5
-
r9 = _nt_CRLF
-
s0 << r9
-
end
-
end
-
end
-
end
-
if s0.last
-
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
r0.extend(ObsReferences0)
-
else
-
@index = i0
-
r0 = nil
-
end
-
-
node_cache[:obs_references][start_index] = r0
-
-
r0
-
end
-
-
1
def _nt_obs_id_left
-
start_index = index
-
if node_cache[:obs_id_left].has_key?(index)
-
cached = node_cache[:obs_id_left][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
r0 = _nt_local_part
-
-
node_cache[:obs_id_left][start_index] = r0
-
-
r0
-
end
-
-
1
def _nt_obs_id_right
-
start_index = index
-
if node_cache[:obs_id_right].has_key?(index)
-
cached = node_cache[:obs_id_right][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
r0 = _nt_domain
-
-
node_cache[:obs_id_right][start_index] = r0
-
-
r0
-
end
-
-
1
module ObsSubject0
-
1
def unstructured
-
elements[3]
-
end
-
-
1
def CRLF
-
elements[4]
-
end
-
end
-
-
1
def _nt_obs_subject
-
start_index = index
-
if node_cache[:obs_subject].has_key?(index)
-
cached = node_cache[:obs_subject][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
i0, s0 = index, []
-
if has_terminal?("Subject", false, index)
-
r1 = instantiate_node(SyntaxNode,input, index...(index + 7))
-
@index += 7
-
else
-
terminal_parse_failure("Subject")
-
r1 = nil
-
end
-
s0 << r1
-
if r1
-
s2, i2 = [], index
-
loop do
-
r3 = _nt_WSP
-
if r3
-
s2 << r3
-
else
-
break
-
end
-
end
-
r2 = instantiate_node(SyntaxNode,input, i2...index, s2)
-
s0 << r2
-
if r2
-
if has_terminal?(":", false, index)
-
r4 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure(":")
-
r4 = nil
-
end
-
s0 << r4
-
if r4
-
r5 = _nt_unstructured
-
s0 << r5
-
if r5
-
r6 = _nt_CRLF
-
s0 << r6
-
end
-
end
-
end
-
end
-
if s0.last
-
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
r0.extend(ObsSubject0)
-
else
-
@index = i0
-
r0 = nil
-
end
-
-
node_cache[:obs_subject][start_index] = r0
-
-
r0
-
end
-
-
1
module ObsComments0
-
1
def unstructured
-
elements[3]
-
end
-
-
1
def CRLF
-
elements[4]
-
end
-
end
-
-
1
def _nt_obs_comments
-
start_index = index
-
if node_cache[:obs_comments].has_key?(index)
-
cached = node_cache[:obs_comments][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
i0, s0 = index, []
-
if has_terminal?("Comments", false, index)
-
r1 = instantiate_node(SyntaxNode,input, index...(index + 8))
-
@index += 8
-
else
-
terminal_parse_failure("Comments")
-
r1 = nil
-
end
-
s0 << r1
-
if r1
-
s2, i2 = [], index
-
loop do
-
r3 = _nt_WSP
-
if r3
-
s2 << r3
-
else
-
break
-
end
-
end
-
r2 = instantiate_node(SyntaxNode,input, i2...index, s2)
-
s0 << r2
-
if r2
-
if has_terminal?(":", false, index)
-
r4 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure(":")
-
r4 = nil
-
end
-
s0 << r4
-
if r4
-
r5 = _nt_unstructured
-
s0 << r5
-
if r5
-
r6 = _nt_CRLF
-
s0 << r6
-
end
-
end
-
end
-
end
-
if s0.last
-
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
r0.extend(ObsComments0)
-
else
-
@index = i0
-
r0 = nil
-
end
-
-
node_cache[:obs_comments][start_index] = r0
-
-
r0
-
end
-
-
1
module ObsKeywords0
-
1
def obs_phrase_list
-
elements[3]
-
end
-
-
1
def CRLF
-
elements[4]
-
end
-
end
-
-
1
def _nt_obs_keywords
-
start_index = index
-
if node_cache[:obs_keywords].has_key?(index)
-
cached = node_cache[:obs_keywords][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
i0, s0 = index, []
-
if has_terminal?("Keywords", false, index)
-
r1 = instantiate_node(SyntaxNode,input, index...(index + 8))
-
@index += 8
-
else
-
terminal_parse_failure("Keywords")
-
r1 = nil
-
end
-
s0 << r1
-
if r1
-
s2, i2 = [], index
-
loop do
-
r3 = _nt_WSP
-
if r3
-
s2 << r3
-
else
-
break
-
end
-
end
-
r2 = instantiate_node(SyntaxNode,input, i2...index, s2)
-
s0 << r2
-
if r2
-
if has_terminal?(":", false, index)
-
r4 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure(":")
-
r4 = nil
-
end
-
s0 << r4
-
if r4
-
r5 = _nt_obs_phrase_list
-
s0 << r5
-
if r5
-
r6 = _nt_CRLF
-
s0 << r6
-
end
-
end
-
end
-
end
-
if s0.last
-
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
r0.extend(ObsKeywords0)
-
else
-
@index = i0
-
r0 = nil
-
end
-
-
node_cache[:obs_keywords][start_index] = r0
-
-
r0
-
end
-
-
1
module ObsResentFrom0
-
1
def mailbox_list
-
elements[3]
-
end
-
-
1
def CRLF
-
elements[4]
-
end
-
end
-
-
1
def _nt_obs_resent_from
-
start_index = index
-
if node_cache[:obs_resent_from].has_key?(index)
-
cached = node_cache[:obs_resent_from][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
i0, s0 = index, []
-
if has_terminal?("Resent-From", false, index)
-
r1 = instantiate_node(SyntaxNode,input, index...(index + 11))
-
@index += 11
-
else
-
terminal_parse_failure("Resent-From")
-
r1 = nil
-
end
-
s0 << r1
-
if r1
-
s2, i2 = [], index
-
loop do
-
r3 = _nt_WSP
-
if r3
-
s2 << r3
-
else
-
break
-
end
-
end
-
r2 = instantiate_node(SyntaxNode,input, i2...index, s2)
-
s0 << r2
-
if r2
-
if has_terminal?(":", false, index)
-
r4 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure(":")
-
r4 = nil
-
end
-
s0 << r4
-
if r4
-
r5 = _nt_mailbox_list
-
s0 << r5
-
if r5
-
r6 = _nt_CRLF
-
s0 << r6
-
end
-
end
-
end
-
end
-
if s0.last
-
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
r0.extend(ObsResentFrom0)
-
else
-
@index = i0
-
r0 = nil
-
end
-
-
node_cache[:obs_resent_from][start_index] = r0
-
-
r0
-
end
-
-
1
module ObsResentSend0
-
1
def mailbox
-
elements[3]
-
end
-
-
1
def CRLF
-
elements[4]
-
end
-
end
-
-
1
def _nt_obs_resent_send
-
start_index = index
-
if node_cache[:obs_resent_send].has_key?(index)
-
cached = node_cache[:obs_resent_send][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
i0, s0 = index, []
-
if has_terminal?("Resent-Sender", false, index)
-
r1 = instantiate_node(SyntaxNode,input, index...(index + 13))
-
@index += 13
-
else
-
terminal_parse_failure("Resent-Sender")
-
r1 = nil
-
end
-
s0 << r1
-
if r1
-
s2, i2 = [], index
-
loop do
-
r3 = _nt_WSP
-
if r3
-
s2 << r3
-
else
-
break
-
end
-
end
-
r2 = instantiate_node(SyntaxNode,input, i2...index, s2)
-
s0 << r2
-
if r2
-
if has_terminal?(":", false, index)
-
r4 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure(":")
-
r4 = nil
-
end
-
s0 << r4
-
if r4
-
r5 = _nt_mailbox
-
s0 << r5
-
if r5
-
r6 = _nt_CRLF
-
s0 << r6
-
end
-
end
-
end
-
end
-
if s0.last
-
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
r0.extend(ObsResentSend0)
-
else
-
@index = i0
-
r0 = nil
-
end
-
-
node_cache[:obs_resent_send][start_index] = r0
-
-
r0
-
end
-
-
1
module ObsResentDate0
-
1
def date_time
-
elements[3]
-
end
-
-
1
def CRLF
-
elements[4]
-
end
-
end
-
-
1
def _nt_obs_resent_date
-
start_index = index
-
if node_cache[:obs_resent_date].has_key?(index)
-
cached = node_cache[:obs_resent_date][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
i0, s0 = index, []
-
if has_terminal?("Resent-Date", false, index)
-
r1 = instantiate_node(SyntaxNode,input, index...(index + 11))
-
@index += 11
-
else
-
terminal_parse_failure("Resent-Date")
-
r1 = nil
-
end
-
s0 << r1
-
if r1
-
s2, i2 = [], index
-
loop do
-
r3 = _nt_WSP
-
if r3
-
s2 << r3
-
else
-
break
-
end
-
end
-
r2 = instantiate_node(SyntaxNode,input, i2...index, s2)
-
s0 << r2
-
if r2
-
if has_terminal?(":", false, index)
-
r4 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure(":")
-
r4 = nil
-
end
-
s0 << r4
-
if r4
-
r5 = _nt_date_time
-
s0 << r5
-
if r5
-
r6 = _nt_CRLF
-
s0 << r6
-
end
-
end
-
end
-
end
-
if s0.last
-
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
r0.extend(ObsResentDate0)
-
else
-
@index = i0
-
r0 = nil
-
end
-
-
node_cache[:obs_resent_date][start_index] = r0
-
-
r0
-
end
-
-
1
module ObsResentTo0
-
1
def address_list
-
elements[3]
-
end
-
-
1
def CRLF
-
elements[4]
-
end
-
end
-
-
1
def _nt_obs_resent_to
-
start_index = index
-
if node_cache[:obs_resent_to].has_key?(index)
-
cached = node_cache[:obs_resent_to][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
i0, s0 = index, []
-
if has_terminal?("Resent-To", false, index)
-
r1 = instantiate_node(SyntaxNode,input, index...(index + 9))
-
@index += 9
-
else
-
terminal_parse_failure("Resent-To")
-
r1 = nil
-
end
-
s0 << r1
-
if r1
-
s2, i2 = [], index
-
loop do
-
r3 = _nt_WSP
-
if r3
-
s2 << r3
-
else
-
break
-
end
-
end
-
r2 = instantiate_node(SyntaxNode,input, i2...index, s2)
-
s0 << r2
-
if r2
-
if has_terminal?(":", false, index)
-
r4 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure(":")
-
r4 = nil
-
end
-
s0 << r4
-
if r4
-
r5 = _nt_address_list
-
s0 << r5
-
if r5
-
r6 = _nt_CRLF
-
s0 << r6
-
end
-
end
-
end
-
end
-
if s0.last
-
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
r0.extend(ObsResentTo0)
-
else
-
@index = i0
-
r0 = nil
-
end
-
-
node_cache[:obs_resent_to][start_index] = r0
-
-
r0
-
end
-
-
1
module ObsResentCc0
-
1
def address_list
-
elements[3]
-
end
-
-
1
def CRLF
-
elements[4]
-
end
-
end
-
-
1
def _nt_obs_resent_cc
-
start_index = index
-
if node_cache[:obs_resent_cc].has_key?(index)
-
cached = node_cache[:obs_resent_cc][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
i0, s0 = index, []
-
if has_terminal?("Resent-Cc", false, index)
-
r1 = instantiate_node(SyntaxNode,input, index...(index + 9))
-
@index += 9
-
else
-
terminal_parse_failure("Resent-Cc")
-
r1 = nil
-
end
-
s0 << r1
-
if r1
-
s2, i2 = [], index
-
loop do
-
r3 = _nt_WSP
-
if r3
-
s2 << r3
-
else
-
break
-
end
-
end
-
r2 = instantiate_node(SyntaxNode,input, i2...index, s2)
-
s0 << r2
-
if r2
-
if has_terminal?(":", false, index)
-
r4 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure(":")
-
r4 = nil
-
end
-
s0 << r4
-
if r4
-
r5 = _nt_address_list
-
s0 << r5
-
if r5
-
r6 = _nt_CRLF
-
s0 << r6
-
end
-
end
-
end
-
end
-
if s0.last
-
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
r0.extend(ObsResentCc0)
-
else
-
@index = i0
-
r0 = nil
-
end
-
-
node_cache[:obs_resent_cc][start_index] = r0
-
-
r0
-
end
-
-
1
module ObsResentBcc0
-
1
def CRLF
-
elements[4]
-
end
-
end
-
-
1
def _nt_obs_resent_bcc
-
start_index = index
-
if node_cache[:obs_resent_bcc].has_key?(index)
-
cached = node_cache[:obs_resent_bcc][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
i0, s0 = index, []
-
if has_terminal?("Resent-Bcc", false, index)
-
r1 = instantiate_node(SyntaxNode,input, index...(index + 10))
-
@index += 10
-
else
-
terminal_parse_failure("Resent-Bcc")
-
r1 = nil
-
end
-
s0 << r1
-
if r1
-
s2, i2 = [], index
-
loop do
-
r3 = _nt_WSP
-
if r3
-
s2 << r3
-
else
-
break
-
end
-
end
-
r2 = instantiate_node(SyntaxNode,input, i2...index, s2)
-
s0 << r2
-
if r2
-
if has_terminal?(":", false, index)
-
r4 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure(":")
-
r4 = nil
-
end
-
s0 << r4
-
if r4
-
i5 = index
-
r6 = _nt_address_list
-
if r6
-
r5 = r6
-
else
-
r8 = _nt_CFWS
-
if r8
-
r7 = r8
-
else
-
r7 = instantiate_node(SyntaxNode,input, index...index)
-
end
-
if r7
-
r5 = r7
-
else
-
@index = i5
-
r5 = nil
-
end
-
end
-
s0 << r5
-
if r5
-
r9 = _nt_CRLF
-
s0 << r9
-
end
-
end
-
end
-
end
-
if s0.last
-
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
r0.extend(ObsResentBcc0)
-
else
-
@index = i0
-
r0 = nil
-
end
-
-
node_cache[:obs_resent_bcc][start_index] = r0
-
-
r0
-
end
-
-
1
module ObsResentMid0
-
1
def msg_id
-
elements[3]
-
end
-
-
1
def CRLF
-
elements[4]
-
end
-
end
-
-
1
def _nt_obs_resent_mid
-
start_index = index
-
if node_cache[:obs_resent_mid].has_key?(index)
-
cached = node_cache[:obs_resent_mid][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
i0, s0 = index, []
-
if has_terminal?("Resent-Message-ID", false, index)
-
r1 = instantiate_node(SyntaxNode,input, index...(index + 17))
-
@index += 17
-
else
-
terminal_parse_failure("Resent-Message-ID")
-
r1 = nil
-
end
-
s0 << r1
-
if r1
-
s2, i2 = [], index
-
loop do
-
r3 = _nt_WSP
-
if r3
-
s2 << r3
-
else
-
break
-
end
-
end
-
r2 = instantiate_node(SyntaxNode,input, i2...index, s2)
-
s0 << r2
-
if r2
-
if has_terminal?(":", false, index)
-
r4 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure(":")
-
r4 = nil
-
end
-
s0 << r4
-
if r4
-
r5 = _nt_msg_id
-
s0 << r5
-
if r5
-
r6 = _nt_CRLF
-
s0 << r6
-
end
-
end
-
end
-
end
-
if s0.last
-
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
r0.extend(ObsResentMid0)
-
else
-
@index = i0
-
r0 = nil
-
end
-
-
node_cache[:obs_resent_mid][start_index] = r0
-
-
r0
-
end
-
-
1
module ObsResentRply0
-
1
def address_list
-
elements[3]
-
end
-
-
1
def CRLF
-
elements[4]
-
end
-
end
-
-
1
def _nt_obs_resent_rply
-
start_index = index
-
if node_cache[:obs_resent_rply].has_key?(index)
-
cached = node_cache[:obs_resent_rply][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
i0, s0 = index, []
-
if has_terminal?("Resent-Reply-To", false, index)
-
r1 = instantiate_node(SyntaxNode,input, index...(index + 15))
-
@index += 15
-
else
-
terminal_parse_failure("Resent-Reply-To")
-
r1 = nil
-
end
-
s0 << r1
-
if r1
-
s2, i2 = [], index
-
loop do
-
r3 = _nt_WSP
-
if r3
-
s2 << r3
-
else
-
break
-
end
-
end
-
r2 = instantiate_node(SyntaxNode,input, i2...index, s2)
-
s0 << r2
-
if r2
-
if has_terminal?(":", false, index)
-
r4 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure(":")
-
r4 = nil
-
end
-
s0 << r4
-
if r4
-
r5 = _nt_address_list
-
s0 << r5
-
if r5
-
r6 = _nt_CRLF
-
s0 << r6
-
end
-
end
-
end
-
end
-
if s0.last
-
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
r0.extend(ObsResentRply0)
-
else
-
@index = i0
-
r0 = nil
-
end
-
-
node_cache[:obs_resent_rply][start_index] = r0
-
-
r0
-
end
-
-
1
module ObsReturn0
-
1
def path
-
elements[3]
-
end
-
-
1
def CRLF
-
elements[4]
-
end
-
end
-
-
1
def _nt_obs_return
-
start_index = index
-
if node_cache[:obs_return].has_key?(index)
-
cached = node_cache[:obs_return][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
i0, s0 = index, []
-
if has_terminal?("Return-Path", false, index)
-
r1 = instantiate_node(SyntaxNode,input, index...(index + 11))
-
@index += 11
-
else
-
terminal_parse_failure("Return-Path")
-
r1 = nil
-
end
-
s0 << r1
-
if r1
-
s2, i2 = [], index
-
loop do
-
r3 = _nt_WSP
-
if r3
-
s2 << r3
-
else
-
break
-
end
-
end
-
r2 = instantiate_node(SyntaxNode,input, i2...index, s2)
-
s0 << r2
-
if r2
-
if has_terminal?(":", false, index)
-
r4 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure(":")
-
r4 = nil
-
end
-
s0 << r4
-
if r4
-
r5 = _nt_path
-
s0 << r5
-
if r5
-
r6 = _nt_CRLF
-
s0 << r6
-
end
-
end
-
end
-
end
-
if s0.last
-
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
r0.extend(ObsReturn0)
-
else
-
@index = i0
-
r0 = nil
-
end
-
-
node_cache[:obs_return][start_index] = r0
-
-
r0
-
end
-
-
1
module ObsReceived0
-
1
def name_val_list
-
elements[3]
-
end
-
-
1
def CRLF
-
elements[4]
-
end
-
end
-
-
1
def _nt_obs_received
-
start_index = index
-
if node_cache[:obs_received].has_key?(index)
-
cached = node_cache[:obs_received][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
i0, s0 = index, []
-
if has_terminal?("Received", false, index)
-
r1 = instantiate_node(SyntaxNode,input, index...(index + 8))
-
@index += 8
-
else
-
terminal_parse_failure("Received")
-
r1 = nil
-
end
-
s0 << r1
-
if r1
-
s2, i2 = [], index
-
loop do
-
r3 = _nt_WSP
-
if r3
-
s2 << r3
-
else
-
break
-
end
-
end
-
r2 = instantiate_node(SyntaxNode,input, i2...index, s2)
-
s0 << r2
-
if r2
-
if has_terminal?(":", false, index)
-
r4 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure(":")
-
r4 = nil
-
end
-
s0 << r4
-
if r4
-
r5 = _nt_name_val_list
-
s0 << r5
-
if r5
-
r6 = _nt_CRLF
-
s0 << r6
-
end
-
end
-
end
-
end
-
if s0.last
-
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
r0.extend(ObsReceived0)
-
else
-
@index = i0
-
r0 = nil
-
end
-
-
node_cache[:obs_received][start_index] = r0
-
-
r0
-
end
-
-
1
def _nt_obs_path
-
start_index = index
-
if node_cache[:obs_path].has_key?(index)
-
cached = node_cache[:obs_path][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
r0 = _nt_obs_angle_addr
-
-
node_cache[:obs_path][start_index] = r0
-
-
r0
-
end
-
-
1
module ObsOptional0
-
1
def field_name
-
elements[0]
-
end
-
-
1
def unstructured
-
elements[3]
-
end
-
-
1
def CRLF
-
elements[4]
-
end
-
end
-
-
1
def _nt_obs_optional
-
start_index = index
-
if node_cache[:obs_optional].has_key?(index)
-
cached = node_cache[:obs_optional][index]
-
if cached
-
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
-
@index = cached.interval.end
-
end
-
return cached
-
end
-
-
i0, s0 = index, []
-
r1 = _nt_field_name
-
s0 << r1
-
if r1
-
s2, i2 = [], index
-
loop do
-
r3 = _nt_WSP
-
if r3
-
s2 << r3
-
else
-
break
-
end
-
end
-
r2 = instantiate_node(SyntaxNode,input, i2...index, s2)
-
s0 << r2
-
if r2
-
if has_terminal?(":", false, index)
-
r4 = instantiate_node(SyntaxNode,input, index...(index + 1))
-
@index += 1
-
else
-
terminal_parse_failure(":")
-
r4 = nil
-
end
-
s0 << r4
-
if r4
-
r5 = _nt_unstructured
-
s0 << r5
-
if r5
-
r6 = _nt_CRLF
-
s0 << r6
-
end
-
end
-
end
-
end
-
if s0.last
-
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
-
r0.extend(ObsOptional0)
-
else
-
@index = i0
-
r0 = nil
-
end
-
-
node_cache[:obs_optional][start_index] = r0
-
-
r0
-
end
-
-
end
-
-
1
class RFC2822ObsoleteParser < Treetop::Runtime::CompiledParser
-
1
include RFC2822Obsolete
-
end
-
-
end
-
1
module Mail
-
1
class PartsList < Array
-
-
1
def attachments
-
276
Mail::AttachmentsList.new(self)
-
end
-
-
1
def collect
-
299
if block_given?
-
299
ary = PartsList.new
-
456
each { |o| ary << yield(o) }
-
299
ary
-
else
-
to_a
-
end
-
end
-
-
1
undef :map
-
1
alias_method :map, :collect
-
-
1
def map!
-
raise NoMethodError, "#map! is not defined, please call #collect and create a new PartsList"
-
end
-
-
1
def collect!
-
raise NoMethodError, "#collect! is not defined, please call #collect and create a new PartsList"
-
end
-
-
1
def sort
-
164
self.class.new(super)
-
end
-
-
1
def sort!(order)
-
164
sorted = self.sort do |a, b|
-
# OK, 10000 is arbitrary... if anyone actually wants to explicitly sort 10000 parts of a
-
# single email message... please show me a use case and I'll put more work into this method,
-
# in the meantime, it works :)
-
87
get_order_value(a, order) <=> get_order_value(b, order)
-
end
-
164
self.clear
-
338
sorted.each { |p| self << p }
-
end
-
-
1
private
-
-
1
def get_order_value(part, order)
-
174
if part.respond_to?(:content_type)
-
174
order.index(part[:content_type].string.downcase) || 10000
-
else
-
10000
-
end
-
end
-
-
end
-
end
-
# encoding: us-ascii
-
1
module Mail
-
1
module Patterns
-
1
white_space = %Q|\x9\x20|
-
1
text = %Q|\x1-\x8\xB\xC\xE-\x7f|
-
1
field_name = %Q|\x21-\x39\x3b-\x7e|
-
1
qp_safe = %Q|\x20-\x3c\x3e-\x7e|
-
-
1
aspecial = %Q|()<>[]:;@\\,."| # RFC5322
-
1
tspecial = %Q|()<>@,;:\\"/[]?=| # RFC2045
-
1
lwsp = %Q| \t\r\n|
-
1
sp = %Q| |
-
1
control = %Q|\x00-\x1f\x7f-\xff|
-
-
1
if control.respond_to?(:force_encoding)
-
1
control = control.force_encoding(Encoding::BINARY)
-
end
-
-
1
CRLF = /\r\n/
-
1
WSP = /[#{white_space}]/
-
1
FWS = /#{CRLF}#{WSP}*/
-
1
TEXT = /[#{text}]/ # + obs-text
-
1
FIELD_NAME = /[#{field_name}]+/
-
1
FIELD_BODY = /.+/
-
1
FIELD_LINE = /^[#{field_name}]+:\s*.+$/
-
1
HEADER_LINE = /^([#{field_name}]+:\s*.+)$/
-
-
1
QP_UNSAFE = /[^#{qp_safe}]/
-
1
QP_SAFE = /[#{qp_safe}]/
-
1
CONTROL_CHAR = /[#{control}]/n
-
1
ATOM_UNSAFE = /[#{Regexp.quote aspecial}#{control}#{sp}]/n
-
1
PHRASE_UNSAFE = /[#{Regexp.quote aspecial}#{control}]/n
-
1
TOKEN_UNSAFE = /[#{Regexp.quote tspecial}#{control}#{sp}]/n
-
end
-
end
-
# encoding: utf-8
-
1
module Mail
-
1
module Utilities
-
1
include Patterns
-
-
# Returns true if the string supplied is free from characters not allowed as an ATOM
-
1
def atom_safe?( str )
-
not ATOM_UNSAFE === str
-
end
-
-
# If the string supplied has ATOM unsafe characters in it, will return the string quoted
-
# in double quotes, otherwise returns the string unmodified
-
1
def quote_atom( str )
-
atom_safe?( str ) ? str : dquote(str)
-
end
-
-
# If the string supplied has PHRASE unsafe characters in it, will return the string quoted
-
# in double quotes, otherwise returns the string unmodified
-
1
def quote_phrase( str )
-
2
if RUBY_VERSION >= '1.9'
-
2
original_encoding = str.encoding
-
2
str.force_encoding('ASCII-8BIT')
-
2
if (PHRASE_UNSAFE === str)
-
dquote(str).force_encoding(original_encoding)
-
else
-
2
str.force_encoding(original_encoding)
-
end
-
else
-
(PHRASE_UNSAFE === str) ? dquote(str) : str
-
end
-
end
-
-
# Returns true if the string supplied is free from characters not allowed as a TOKEN
-
1
def token_safe?( str )
-
490
not TOKEN_UNSAFE === str
-
end
-
-
# If the string supplied has TOKEN unsafe characters in it, will return the string quoted
-
# in double quotes, otherwise returns the string unmodified
-
1
def quote_token( str )
-
490
token_safe?( str ) ? str : dquote(str)
-
end
-
-
# Wraps supplied string in double quotes unless it is already wrapped.
-
#
-
# Additionally will escape any double quotation marks in the string with a single
-
# backslash in front of the '"' character.
-
1
def dquote( str )
-
48
match = str.match(/^"(.*)?"$/)
-
48
str = match[1] if match
-
# First remove all escaped double quotes:
-
48
str = str.gsub(/\\"/, '"')
-
# Then wrap and re-escape all double quotes
-
48
'"' + str.gsub(/["]/n) {|s| '\\' + s } + '"'
-
end
-
-
# Unwraps supplied string from inside double quotes.
-
#
-
# Example:
-
#
-
# string = '"This is a string"'
-
# unquote(string) #=> 'This is a string'
-
1
def unquote( str )
-
1
match = str.match(/^"(.*?)"$/)
-
1
match ? match[1] : str
-
end
-
-
# Wraps a string in parenthesis and escapes any that are in the string itself.
-
#
-
# Example:
-
#
-
# paren( 'This is a string' ) #=> '(This is a string)'
-
1
def paren( str )
-
RubyVer.paren( str )
-
end
-
-
# Unwraps a string from being wrapped in parenthesis
-
#
-
# Example:
-
#
-
# str = '(This is a string)'
-
# unparen( str ) #=> 'This is a string'
-
1
def unparen( str )
-
match = str.match(/^\((.*?)\)$/)
-
match ? match[1] : str
-
end
-
-
# Wraps a string in angle brackets and escapes any that are in the string itself
-
#
-
# Example:
-
#
-
# bracket( 'This is a string' ) #=> '<This is a string>'
-
1
def bracket( str )
-
RubyVer.bracket( str )
-
end
-
-
# Unwraps a string from being wrapped in parenthesis
-
#
-
# Example:
-
#
-
# str = '<This is a string>'
-
# unbracket( str ) #=> 'This is a string'
-
1
def unbracket( str )
-
1
match = str.match(/^\<(.*?)\>$/)
-
1
match ? match[1] : str
-
end
-
-
# Escape parenthesies in a string
-
#
-
# Example:
-
#
-
# str = 'This is (a) string'
-
# escape_paren( str ) #=> 'This is \(a\) string'
-
1
def escape_paren( str )
-
RubyVer.escape_paren( str )
-
end
-
-
1
def uri_escape( str )
-
1
uri_parser.escape(str)
-
end
-
-
1
def uri_unescape( str )
-
uri_parser.unescape(str)
-
end
-
-
1
def uri_parser
-
1
@uri_parser ||= URI.const_defined?(:Parser) ? URI::Parser.new : URI
-
end
-
-
# Matches two objects with their to_s values case insensitively
-
#
-
# Example:
-
#
-
# obj2 = "This_is_An_object"
-
# obj1 = :this_IS_an_object
-
# match_to_s( obj1, obj2 ) #=> true
-
1
def match_to_s( obj1, obj2 )
-
2758
obj1.to_s.downcase == obj2.to_s.downcase
-
end
-
-
# Capitalizes a string that is joined by hyphens correctly.
-
#
-
# Example:
-
#
-
# string = 'resent-from-field'
-
# capitalize_field( string ) #=> 'Resent-From-Field'
-
1
def capitalize_field( str )
-
str.to_s.split("-").map { |v| v.capitalize }.join("-")
-
end
-
-
# Takes an underscored word and turns it into a class name
-
#
-
# Example:
-
#
-
# constantize("hello") #=> "Hello"
-
# constantize("hello-there") #=> "HelloThere"
-
# constantize("hello-there-mate") #=> "HelloThereMate"
-
1
def constantize( str )
-
str.to_s.split(/[-_]/).map { |v| v.capitalize }.to_s
-
end
-
-
# Swaps out all underscores (_) for hyphens (-) good for stringing from symbols
-
# a field name.
-
#
-
# Example:
-
#
-
# string = :resent_from_field
-
# dasherize ( string ) #=> 'resent_from_field'
-
1
def dasherize( str )
-
7509
str.to_s.gsub('_', '-')
-
end
-
-
# Swaps out all hyphens (-) for underscores (_) good for stringing to symbols
-
# a field name.
-
#
-
# Example:
-
#
-
# string = :resent_from_field
-
# underscoreize ( string ) #=> 'resent_from_field'
-
1
def underscoreize( str )
-
451
str.to_s.downcase.gsub('-', '_')
-
end
-
-
1
if RUBY_VERSION <= '1.8.6'
-
-
def map_lines( str, &block )
-
results = []
-
str.each_line do |line|
-
results << yield(line)
-
end
-
results
-
end
-
-
def map_with_index( enum, &block )
-
results = []
-
enum.each_with_index do |token, i|
-
results[i] = yield(token, i)
-
end
-
results
-
end
-
-
else
-
-
1
def map_lines( str, &block )
-
str.each_line.map(&block)
-
end
-
-
1
def map_with_index( enum, &block )
-
enum.each_with_index.map(&block)
-
end
-
-
end
-
-
end
-
end
-
# encoding: utf-8
-
1
module Mail
-
1
module VERSION
-
-
1
version = {}
-
1
File.read(File.join(File.dirname(__FILE__), '../', 'VERSION')).each_line do |line|
-
4
type, value = line.chomp.split(":")
-
4
next if type =~ /^\s+$/ || value =~ /^\s+$/
-
4
version[type] = value
-
end
-
-
1
MAJOR = version['major']
-
1
MINOR = version['minor']
-
1
PATCH = version['patch']
-
1
BUILD = version['build']
-
-
1
STRING = [MAJOR, MINOR, PATCH, BUILD].compact.join('.')
-
-
1
def self.version
-
STRING
-
end
-
-
end
-
end
-
# encoding: utf-8
-
-
1
module Mail
-
1
class Ruby19
-
-
# Escapes any parenthesis in a string that are unescaped this uses
-
# a Ruby 1.9.1 regexp feature of negative look behind
-
1
def Ruby19.escape_paren( str )
-
re = /(?<!\\)([\(\)])/ # Only match unescaped parens
-
str.gsub(re) { |s| '\\' + s }
-
end
-
-
1
def Ruby19.paren( str )
-
str = $1 if str =~ /^\((.*)?\)$/
-
str = escape_paren( str )
-
'(' + str + ')'
-
end
-
-
1
def Ruby19.escape_bracket( str )
-
re = /(?<!\\)([\<\>])/ # Only match unescaped brackets
-
str.gsub(re) { |s| '\\' + s }
-
end
-
-
1
def Ruby19.bracket( str )
-
str = $1 if str =~ /^\<(.*)?\>$/
-
str = escape_bracket( str )
-
'<' + str + '>'
-
end
-
-
1
def Ruby19.decode_base64(str)
-
1
str.unpack( 'm' ).first
-
end
-
-
1
def Ruby19.encode_base64(str)
-
4
[str].pack( 'm' )
-
end
-
-
1
def Ruby19.has_constant?(klass, string)
-
klass.const_defined?( string, false )
-
end
-
-
1
def Ruby19.get_constant(klass, string)
-
klass.const_get( string )
-
end
-
-
1
def Ruby19.b_value_encode(str, encoding = nil)
-
encoding = str.encoding.to_s
-
[Ruby19.encode_base64(str), encoding]
-
end
-
-
1
def Ruby19.b_value_decode(str)
-
match = str.match(/\=\?(.+)?\?[Bb]\?(.+)?\?\=/m)
-
if match
-
encoding = match[1]
-
str = Ruby19.decode_base64(match[2])
-
str.force_encoding(fix_encoding(encoding))
-
end
-
decoded = str.encode("utf-8", :invalid => :replace, :replace => "")
-
decoded.valid_encoding? ? decoded : decoded.encode("utf-16le", :invalid => :replace, :replace => "").encode("utf-8")
-
end
-
-
1
def Ruby19.q_value_encode(str, encoding = nil)
-
encoding = str.encoding.to_s
-
[Encodings::QuotedPrintable.encode(str), encoding]
-
end
-
-
1
def Ruby19.q_value_decode(str)
-
2
match = str.match(/\=\?(.+)?\?[Qq]\?(.+)?\?\=/m)
-
2
if match
-
2
encoding = match[1]
-
2
str = Encodings::QuotedPrintable.decode(match[2].gsub(/_/, '=20'))
-
2
str.force_encoding(fix_encoding(encoding))
-
end
-
2
decoded = str.encode("utf-8", :invalid => :replace, :replace => "")
-
2
decoded.valid_encoding? ? decoded : decoded.encode("utf-16le", :invalid => :replace, :replace => "").encode("utf-8")
-
end
-
-
1
def Ruby19.param_decode(str, encoding)
-
string = uri_parser.unescape(str)
-
string.force_encoding(encoding) if encoding
-
string
-
end
-
-
1
def Ruby19.param_encode(str)
-
encoding = str.encoding.to_s.downcase
-
language = Configuration.instance.param_encode_language
-
"#{encoding}'#{language}'#{uri_parser.escape(str)}"
-
end
-
-
1
def Ruby19.uri_parser
-
@uri_parser ||= URI::Parser.new
-
end
-
-
# mails somtimes includes invalid encodings like iso885915 or utf8 so we transform them to iso885915 or utf8
-
# TODO: add this as a test somewhere
-
# Encoding.list.map{|e| [e.to_s.upcase==fix_encoding(e.to_s.downcase.gsub("-", "")), e.to_s] }.select {|a,b| !b}
-
# Encoding.list.map{|e| [e.to_s==fix_encoding(e.to_s), e.to_s] }.select {|a,b| !b}
-
1
def Ruby19.fix_encoding(encoding)
-
2
case encoding
-
# ISO-8859-15, ISO-2022-JP and alike
-
when /iso-?(\d{4})-?(\w{1,2})/i then return "ISO-#{$1}-#{$2}"
-
# "ISO-2022-JP-KDDI" and alike
-
when /iso-?(\d{4})-?(\w{1,2})-?(\w*)/i then return "ISO-#{$1}-#{$2}-#{$3}"
-
# UTF-8, UTF-32BE and alike
-
when /utf-?(\d{1,2})?(\w{1,2})/i then return "UTF-#{$1}#{$2}".gsub(/\A(UTF-(?:16|32))\z/, '\\1BE')
-
# Windows-1252 and alike
-
when /Windows-?(.*)/i then return "Windows-#{$1}"
-
#more aliases to be added if needed
-
2
else return encoding
-
end
-
end
-
end
-
end
-
1
module Metaclass
-
end
-
-
1
require "metaclass/version"
-
1
require "metaclass/object_methods"
-
1
module Metaclass::ObjectMethods
-
1
def __metaclass__
-
121
class << self
-
121
self
-
end
-
end
-
end
-
-
1
class Object
-
1
include Metaclass::ObjectMethods
-
end
-
1
module Metaclass
-
1
VERSION = "0.0.1"
-
end
-
# -*- ruby encoding: utf-8 -*-
-
-
# The namespace for MIME applications, tools, and libraries.
-
1
module MIME
-
# Reflects a MIME Content-Type which is in invalid format (e.g., it isn't
-
# in the form of type/subtype).
-
1
class InvalidContentType < RuntimeError; end
-
-
# The definition of one MIME content-type.
-
#
-
# == Usage
-
# require 'mime/types'
-
#
-
# plaintext = MIME::Types['text/plain']
-
# print plaintext.media_type # => 'text'
-
# print plaintext.sub_type # => 'plain'
-
#
-
# puts plaintext.extensions.join(" ") # => 'asc txt c cc h hh cpp'
-
#
-
# puts plaintext.encoding # => 8bit
-
# puts plaintext.binary? # => false
-
# puts plaintext.ascii? # => true
-
# puts plaintext == 'text/plain' # => true
-
# puts MIME::Type.simplified('x-appl/x-zip') # => 'appl/zip'
-
#
-
1
class Type
-
# The released version of Ruby MIME::Types
-
1
VERSION = '1.19'
-
-
1
include Comparable
-
-
1
MEDIA_TYPE_RE = %r{([-\w.+]+)/([-\w.+]*)}o
-
1
UNREG_RE = %r{[Xx]-}o
-
1
ENCODING_RE = %r{(?:base64|7bit|8bit|quoted\-printable)}o
-
1
PLATFORM_RE = %r|#{RUBY_PLATFORM}|o
-
-
1
SIGNATURES = %w(application/pgp-keys application/pgp
-
application/pgp-signature application/pkcs10
-
application/pkcs7-mime application/pkcs7-signature
-
text/vcard)
-
-
1
IANA_URL = "http://www.iana.org/assignments/media-types/%s/%s"
-
1
RFC_URL = "http://rfc-editor.org/rfc/rfc%s.txt"
-
1
DRAFT_URL = "http://datatracker.ietf.org/public/idindex.cgi?command=id_details&filename=%s"
-
1
LTSW_URL = "http://www.ltsw.se/knbase/internet/%s.htp"
-
1
CONTACT_URL = "http://www.iana.org/assignments/contact-people.htm#%s"
-
-
# Returns +true+ if the simplified type matches the current
-
1
def like?(other)
-
if other.respond_to?(:simplified)
-
@simplified == other.simplified
-
else
-
@simplified == Type.simplified(other)
-
end
-
end
-
-
# Compares the MIME::Type against the exact content type or the
-
# simplified type (the simplified type will be used if comparing against
-
# something that can be treated as a String with #to_s). In comparisons,
-
# this is done against the lowercase version of the MIME::Type.
-
1
def <=>(other)
-
69
if other.respond_to?(:content_type)
-
69
@content_type.downcase <=> other.content_type.downcase
-
elsif other.respond_to?(:to_s)
-
@simplified <=> Type.simplified(other.to_s)
-
else
-
@content_type.downcase <=> other.downcase
-
end
-
end
-
-
# Compares the MIME::Type based on how reliable it is before doing a
-
# normal <=> comparison. Used by MIME::Types#[] to sort types. The
-
# comparisons involved are:
-
#
-
# 1. self.simplified <=> other.simplified (ensures that we
-
# don't try to compare different types)
-
# 2. IANA-registered definitions > other definitions.
-
# 3. Generic definitions > platform definitions.
-
# 3. Complete definitions > incomplete definitions.
-
# 4. Current definitions > obsolete definitions.
-
# 5. Obselete with use-instead references > obsolete without.
-
# 6. Obsolete use-instead definitions are compared.
-
1
def priority_compare(other)
-
pc = simplified <=> other.simplified
-
-
if pc.zero? and registered? != other.registered?
-
pc = registered? ? -1 : 1
-
end
-
-
if pc.zero? and platform? != other.platform?
-
pc = platform? ? 1 : -1
-
end
-
-
if pc.zero? and complete? != other.complete?
-
pc = complete? ? -1 : 1
-
end
-
-
if pc.zero? and obsolete? != other.obsolete?
-
pc = obsolete? ? 1 : -1
-
end
-
-
if pc.zero? and obsolete? and (use_instead != other.use_instead)
-
pc = if use_instead.nil?
-
-1
-
elsif other.use_instead.nil?
-
1
-
else
-
use_instead <=> other.use_instead
-
end
-
end
-
-
pc
-
end
-
-
# Returns +true+ if the other object is a MIME::Type and the content
-
# types match.
-
1
def eql?(other)
-
other.kind_of?(MIME::Type) and self == other
-
end
-
-
# Returns the whole MIME content-type string.
-
#
-
# text/plain => text/plain
-
# x-chemical/x-pdb => x-chemical/x-pdb
-
1
attr_reader :content_type
-
# Returns the media type of the simplified MIME type.
-
#
-
# text/plain => text
-
# x-chemical/x-pdb => chemical
-
1
attr_reader :media_type
-
# Returns the media type of the unmodified MIME type.
-
#
-
# text/plain => text
-
# x-chemical/x-pdb => x-chemical
-
1
attr_reader :raw_media_type
-
# Returns the sub-type of the simplified MIME type.
-
#
-
# text/plain => plain
-
# x-chemical/x-pdb => pdb
-
1
attr_reader :sub_type
-
# Returns the media type of the unmodified MIME type.
-
#
-
# text/plain => plain
-
# x-chemical/x-pdb => x-pdb
-
1
attr_reader :raw_sub_type
-
# The MIME types main- and sub-label can both start with <tt>x-</tt>,
-
# which indicates that it is a non-registered name. Of course, after
-
# registration this flag can disappear, adds to the confusing
-
# proliferation of MIME types. The simplified string has the <tt>x-</tt>
-
# removed and are translated to lowercase.
-
#
-
# text/plain => text/plain
-
# x-chemical/x-pdb => chemical/pdb
-
1
attr_reader :simplified
-
# The list of extensions which are known to be used for this MIME::Type.
-
# Non-array values will be coerced into an array with #to_a. Array
-
# values will be flattened and +nil+ values removed.
-
1
attr_accessor :extensions
-
1
remove_method :extensions= ;
-
1
def extensions=(ext) #:nodoc:
-
3066
@extensions = [ext].flatten.compact
-
end
-
-
# The encoding (7bit, 8bit, quoted-printable, or base64) required to
-
# transport the data of this content type safely across a network, which
-
# roughly corresponds to Content-Transfer-Encoding. A value of +nil+ or
-
# <tt>:default</tt> will reset the #encoding to the #default_encoding
-
# for the MIME::Type. Raises ArgumentError if the encoding provided is
-
# invalid.
-
#
-
# If the encoding is not provided on construction, this will be either
-
# 'quoted-printable' (for text/* media types) and 'base64' for eveything
-
# else.
-
1
attr_accessor :encoding
-
1
remove_method :encoding= ;
-
1
def encoding=(enc) #:nodoc:
-
3066
if enc.nil? or enc == :default
-
2913
@encoding = self.default_encoding
-
153
elsif enc =~ ENCODING_RE
-
153
@encoding = enc
-
else
-
raise ArgumentError, "The encoding must be nil, :default, base64, 7bit, 8bit, or quoted-printable."
-
end
-
end
-
-
# The regexp for the operating system that this MIME::Type is specific
-
# to.
-
1
attr_accessor :system
-
1
remove_method :system= ;
-
1
def system=(os) #:nodoc:
-
3066
if os.nil? or os.kind_of?(Regexp)
-
3063
@system = os
-
else
-
3
@system = %r|#{os}|
-
end
-
end
-
# Returns the default encoding for the MIME::Type based on the media
-
# type.
-
1
attr_reader :default_encoding
-
1
remove_method :default_encoding
-
1
def default_encoding
-
2913
(@media_type == 'text') ? 'quoted-printable' : 'base64'
-
end
-
-
# Returns the media type or types that should be used instead of this
-
# media type, if it is obsolete. If there is no replacement media type,
-
# or it is not obsolete, +nil+ will be returned.
-
1
attr_reader :use_instead
-
1
remove_method :use_instead
-
1
def use_instead
-
return nil unless @obsolete
-
@use_instead
-
end
-
-
# Returns +true+ if the media type is obsolete.
-
1
def obsolete?
-
@obsolete ? true : false
-
end
-
# Sets the obsolescence indicator for this media type.
-
1
attr_writer :obsolete
-
-
# The documentation for this MIME::Type. Documentation about media
-
# types will be found on a media type definition as a comment.
-
# Documentation will be found through #docs.
-
1
attr_accessor :docs
-
1
remove_method :docs= ;
-
1
def docs=(d)
-
3066
if d
-
45
a = d.scan(%r{use-instead:#{MEDIA_TYPE_RE}})
-
-
45
if a.empty?
-
2
@use_instead = nil
-
else
-
86
@use_instead = a.map { |el| "#{el[0]}/#{el[1]}" }
-
end
-
end
-
3066
@docs = d
-
end
-
-
# The encoded URL list for this MIME::Type. See #urls for more
-
# information.
-
1
attr_accessor :url
-
# The decoded URL list for this MIME::Type.
-
# The special URL value IANA will be translated into:
-
# http://www.iana.org/assignments/media-types/<mediatype>/<subtype>
-
#
-
# The special URL value RFC### will be translated into:
-
# http://www.rfc-editor.org/rfc/rfc###.txt
-
#
-
# The special URL value DRAFT:name will be translated into:
-
# https://datatracker.ietf.org/public/idindex.cgi?
-
# command=id_detail&filename=<name>
-
#
-
# The special URL value LTSW will be translated into:
-
# http://www.ltsw.se/knbase/internet/<mediatype>.htp
-
#
-
# The special URL value [token] will be translated into:
-
# http://www.iana.org/assignments/contact-people.htm#<token>
-
#
-
# These values will be accessible through #urls, which always returns an
-
# array.
-
1
def urls
-
@url.map do |el|
-
case el
-
when %r{^IANA$}
-
IANA_URL % [ @media_type, @sub_type ]
-
when %r{^RFC(\d+)$}
-
RFC_URL % $1
-
when %r{^DRAFT:(.+)$}
-
DRAFT_URL % $1
-
when %r{^LTSW$}
-
LTSW_URL % @media_type
-
when %r<^\{([^=]+)=([^\]]+)\}>
-
[$1, $2]
-
when %r{^\[([^=]+)=([^\]]+)\]}
-
[$1, CONTACT_URL % $2]
-
when %r{^\[([^\]]+)\]}
-
CONTACT_URL % $1
-
else
-
el
-
end
-
end
-
end
-
-
1
class << self
-
# The MIME types main- and sub-label can both start with <tt>x-</tt>,
-
# which indicates that it is a non-registered name. Of course, after
-
# registration this flag can disappear, adds to the confusing
-
# proliferation of MIME types. The simplified string has the
-
# <tt>x-</tt> removed and are translated to lowercase.
-
1
def simplified(content_type)
-
1535
matchdata = MEDIA_TYPE_RE.match(content_type)
-
-
1535
if matchdata.nil?
-
simplified = nil
-
else
-
1535
media_type = matchdata.captures[0].downcase.gsub(UNREG_RE, '')
-
1535
subtype = matchdata.captures[1].downcase.gsub(UNREG_RE, '')
-
1535
simplified = "#{media_type}/#{subtype}"
-
end
-
1535
simplified
-
end
-
-
# Creates a MIME::Type from an array in the form of:
-
# [type-name, [extensions], encoding, system]
-
#
-
# +extensions+, +encoding+, and +system+ are optional.
-
#
-
# MIME::Type.from_array("application/x-ruby", ['rb'], '8bit')
-
# MIME::Type.from_array(["application/x-ruby", ['rb'], '8bit'])
-
#
-
# These are equivalent to:
-
#
-
# MIME::Type.new('application/x-ruby') do |t|
-
# t.extensions = %w(rb)
-
# t.encoding = '8bit'
-
# end
-
1
def from_array(*args) #:yields MIME::Type.new:
-
# Dereferences the array one level, if necessary.
-
args = args[0] if args[0].kind_of?(Array)
-
-
if args.size.between?(1, 8)
-
m = MIME::Type.new(args[0]) do |t|
-
t.extensions = args[1] if args.size > 1
-
t.encoding = args[2] if args.size > 2
-
t.system = args[3] if args.size > 3
-
t.obsolete = args[4] if args.size > 4
-
t.docs = args[5] if args.size > 5
-
t.url = args[6] if args.size > 6
-
t.registered = args[7] if args.size > 7
-
end
-
yield m if block_given?
-
else
-
raise ArgumentError, "Array provided must contain between one and eight elements."
-
end
-
m
-
end
-
-
# Creates a MIME::Type from a hash. Keys are case-insensitive,
-
# dashes may be replaced with underscores, and the internal Symbol
-
# of the lowercase-underscore version can be used as well. That is,
-
# Content-Type can be provided as content-type, Content_Type,
-
# content_type, or :content_type.
-
#
-
# Known keys are <tt>Content-Type</tt>,
-
# <tt>Content-Transfer-Encoding</tt>, <tt>Extensions</tt>, and
-
# <tt>System</tt>.
-
#
-
# MIME::Type.from_hash('Content-Type' => 'text/x-yaml',
-
# 'Content-Transfer-Encoding' => '8bit',
-
# 'System' => 'linux',
-
# 'Extensions' => ['yaml', 'yml'])
-
#
-
# This is equivalent to:
-
#
-
# MIME::Type.new('text/x-yaml') do |t|
-
# t.encoding = '8bit'
-
# t.system = 'linux'
-
# t.extensions = ['yaml', 'yml']
-
# end
-
1
def from_hash(hash) #:yields MIME::Type.new:
-
type = {}
-
hash.each_pair do |k, v|
-
type[k.to_s.tr('A-Z', 'a-z').gsub(/-/, '_').to_sym] = v
-
end
-
-
m = MIME::Type.new(type[:content_type]) do |t|
-
t.extensions = type[:extensions]
-
t.encoding = type[:content_transfer_encoding]
-
t.system = type[:system]
-
t.obsolete = type[:obsolete]
-
t.docs = type[:docs]
-
t.url = type[:url]
-
t.registered = type[:registered]
-
end
-
-
yield m if block_given?
-
m
-
end
-
-
# Essentially a copy constructor.
-
#
-
# MIME::Type.from_mime_type(plaintext)
-
#
-
# is equivalent to:
-
#
-
# MIME::Type.new(plaintext.content_type.dup) do |t|
-
# t.extensions = plaintext.extensions.dup
-
# t.system = plaintext.system.dup
-
# t.encoding = plaintext.encoding.dup
-
# end
-
1
def from_mime_type(mime_type) #:yields the new MIME::Type:
-
m = MIME::Type.new(mime_type.content_type.dup) do |t|
-
t.extensions = mime_type.extensions.map { |e| e.dup }
-
t.url = mime_type.url && mime_type.url.map { |e| e.dup }
-
-
mime_type.system && t.system = mime_type.system.dup
-
mime_type.encoding && t.encoding = mime_type.encoding.dup
-
-
t.obsolete = mime_type.obsolete?
-
t.registered = mime_type.registered?
-
-
mime_type.docs && t.docs = mime_type.docs.dup
-
-
end
-
-
yield m if block_given?
-
end
-
end
-
-
# Builds a MIME::Type object from the provided MIME Content Type value
-
# (e.g., 'text/plain' or 'applicaton/x-eruby'). The constructed object
-
# is yielded to an optional block for additional configuration, such as
-
# associating extensions and encoding information.
-
1
def initialize(content_type) #:yields self:
-
1533
matchdata = MEDIA_TYPE_RE.match(content_type)
-
-
1533
if matchdata.nil?
-
raise InvalidContentType, "Invalid Content-Type provided ('#{content_type}')"
-
end
-
-
1533
@content_type = content_type
-
1533
@raw_media_type = matchdata.captures[0]
-
1533
@raw_sub_type = matchdata.captures[1]
-
-
1533
@simplified = MIME::Type.simplified(@content_type)
-
1533
matchdata = MEDIA_TYPE_RE.match(@simplified)
-
1533
@media_type = matchdata.captures[0]
-
1533
@sub_type = matchdata.captures[1]
-
-
1533
self.extensions = nil
-
1533
self.encoding = :default
-
1533
self.system = nil
-
1533
self.registered = true
-
1533
self.url = nil
-
1533
self.obsolete = nil
-
1533
self.docs = nil
-
-
1533
yield self if block_given?
-
end
-
-
# MIME content-types which are not regestered by IANA nor defined in
-
# RFCs are required to start with <tt>x-</tt>. This counts as well for
-
# a new media type as well as a new sub-type of an existing media
-
# type. If either the media-type or the content-type begins with
-
# <tt>x-</tt>, this method will return +false+.
-
1
def registered?
-
if (@raw_media_type =~ UNREG_RE) || (@raw_sub_type =~ UNREG_RE)
-
false
-
else
-
@registered
-
end
-
end
-
1
attr_writer :registered #:nodoc:
-
-
# MIME types can be specified to be sent across a network in particular
-
# formats. This method returns +true+ when the MIME type encoding is set
-
# to <tt>base64</tt>.
-
1
def binary?
-
12
@encoding == 'base64'
-
end
-
-
# MIME types can be specified to be sent across a network in particular
-
# formats. This method returns +false+ when the MIME type encoding is
-
# set to <tt>base64</tt>.
-
1
def ascii?
-
not binary?
-
end
-
-
# Returns +true+ when the simplified MIME type is in the list of known
-
# digital signatures.
-
1
def signature?
-
SIGNATURES.include?(@simplified.downcase)
-
end
-
-
# Returns +true+ if the MIME::Type is specific to an operating system.
-
1
def system?
-
not @system.nil?
-
end
-
-
# Returns +true+ if the MIME::Type is specific to the current operating
-
# system as represented by RUBY_PLATFORM.
-
1
def platform?
-
system? and (RUBY_PLATFORM =~ @system)
-
end
-
-
# Returns +true+ if the MIME::Type specifies an extension list,
-
# indicating that it is a complete MIME::Type.
-
1
def complete?
-
not @extensions.empty?
-
end
-
-
# Returns the MIME type as a string.
-
1
def to_s
-
12
@content_type
-
end
-
-
# Returns the MIME type as a string for implicit conversions.
-
1
def to_str
-
@content_type
-
end
-
-
# Returns the MIME type as an array suitable for use with
-
# MIME::Type.from_array.
-
1
def to_a
-
[ @content_type, @extensions, @encoding, @system, @obsolete, @docs,
-
@url, registered? ]
-
end
-
-
# Returns the MIME type as an array suitable for use with
-
# MIME::Type.from_hash.
-
1
def to_hash
-
{ 'Content-Type' => @content_type,
-
'Content-Transfer-Encoding' => @encoding,
-
'Extensions' => @extensions,
-
'System' => @system,
-
'Obsolete' => @obsolete,
-
'Docs' => @docs,
-
'URL' => @url,
-
'Registered' => registered?,
-
}
-
end
-
end
-
-
# = MIME::Types
-
# MIME types are used in MIME-compliant communications, as in e-mail or
-
# HTTP traffic, to indicate the type of content which is transmitted.
-
# MIME::Types provides the ability for detailed information about MIME
-
# entities (provided as a set of MIME::Type objects) to be determined and
-
# used programmatically. There are many types defined by RFCs and vendors,
-
# so the list is long but not complete; don't hesitate to ask to add
-
# additional information. This library follows the IANA collection of MIME
-
# types (see below for reference).
-
#
-
# == Description
-
# MIME types are used in MIME entities, as in email or HTTP traffic. It is
-
# useful at times to have information available about MIME types (or,
-
# inversely, about files). A MIME::Type stores the known information about
-
# one MIME type.
-
#
-
# == Usage
-
# require 'mime/types'
-
#
-
# plaintext = MIME::Types['text/plain']
-
# print plaintext.media_type # => 'text'
-
# print plaintext.sub_type # => 'plain'
-
#
-
# puts plaintext.extensions.join(" ") # => 'asc txt c cc h hh cpp'
-
#
-
# puts plaintext.encoding # => 8bit
-
# puts plaintext.binary? # => false
-
# puts plaintext.ascii? # => true
-
# puts plaintext.obsolete? # => false
-
# puts plaintext.registered? # => true
-
# puts plaintext == 'text/plain' # => true
-
# puts MIME::Type.simplified('x-appl/x-zip') # => 'appl/zip'
-
#
-
# This module is built to conform to the MIME types of RFCs 2045 and 2231.
-
# It follows the official IANA registry at
-
# http://www.iana.org/assignments/media-types/ and
-
# ftp://ftp.iana.org/assignments/media-types with some unofficial types
-
# added from the the collection at
-
# http://www.ltsw.se/knbase/internet/mime.htp
-
#
-
# This is originally based on Perl MIME::Types by Mark Overmeer.
-
#
-
# = Author
-
# Copyright:: Copyright 2002–2012 by Austin Ziegler
-
# <austin@rubyforge.org>
-
# Version:: 1.19
-
# Based On:: Perl
-
# MIME::Types[http://search.cpan.org/author/MARKOV/MIME-Types-1.27/MIME/Types.pm],
-
# Copyright 2001–2009 by Mark Overmeer
-
# <mimetypes@overmeer.net>.
-
# Licence:: Ruby's, Perl Artistic, or GPL version 2 (or later)
-
# See Also:: http://www.iana.org/assignments/media-types/
-
# http://www.ltsw.se/knbase/internet/mime.htp
-
#
-
1
class Types
-
# The released version of Ruby MIME::Types
-
1
VERSION = MIME::Type::VERSION
-
-
# The data version.
-
1
attr_reader :data_version
-
-
1
def initialize(data_version = nil)
-
3024
@type_variants = Hash.new { |h, k| h[k] = [] }
-
1135
@extension_index = Hash.new { |h, k| h[k] = [] }
-
25
@data_version = data_version
-
end
-
-
1
def add_type_variant(mime_type) #:nodoc:
-
3066
@type_variants[mime_type.simplified] << mime_type
-
end
-
-
1
def index_extensions(mime_type) #:nodoc:
-
4300
mime_type.extensions.each { |ext| @extension_index[ext] << mime_type }
-
end
-
-
1
def defined_types #:nodoc:
-
24
@type_variants.values.flatten
-
end
-
-
1
@__types__ = self.new(VERSION)
-
-
# Returns a list of MIME::Type objects, which may be empty. The optional
-
# flag parameters are :complete (finds only complete MIME::Type objects)
-
# and :platform (finds only MIME::Types for the current platform). It is
-
# possible for multiple matches to be returned for either type (in the
-
# example below, 'text/plain' returns two values -- one for the general
-
# case, and one for VMS systems.
-
#
-
# puts "\nMIME::Types['text/plain']"
-
# MIME::Types['text/plain'].each { |t| puts t.to_a.join(", ") }
-
#
-
# puts "\nMIME::Types[/^image/, :complete => true]"
-
# MIME::Types[/^image/, :complete => true].each do |t|
-
# puts t.to_a.join(", ")
-
# end
-
#
-
# If multiple type definitions are returned, returns them sorted as
-
# follows:
-
# 1. Complete definitions sort before incomplete ones;
-
# 2. IANA-registered definitions sort before LTSW-recorded
-
# definitions.
-
# 3. Generic definitions sort before platform-specific ones;
-
# 4. Current definitions sort before obsolete ones;
-
# 5. Obsolete definitions with use-instead clauses sort before those
-
# without;
-
# 6. Obsolete definitions use-instead clauses are compared.
-
# 7. Sort on name.
-
1
def [](type_id, flags = {})
-
2
if type_id.kind_of?(Regexp)
-
matches = []
-
@type_variants.each_key do |k|
-
matches << @type_variants[k] if k =~ type_id
-
end
-
matches.flatten!
-
elsif type_id.kind_of?(MIME::Type)
-
matches = [type_id]
-
else
-
2
matches = @type_variants[MIME::Type.simplified(type_id)]
-
end
-
-
2
matches.delete_if { |e| not e.complete? } if flags[:complete]
-
2
matches.delete_if { |e| not e.platform? } if flags[:platform]
-
-
2
matches.sort { |a, b| a.priority_compare(b) }
-
end
-
-
# Return the list of MIME::Types which belongs to the file based on its
-
# filename extension. If +platform+ is +true+, then only file types that
-
# are specific to the current platform will be returned.
-
#
-
# This will always return an array.
-
#
-
# puts "MIME::Types.type_for('citydesk.xml')
-
# => [application/xml, text/xml]
-
# puts "MIME::Types.type_for('citydesk.gif')
-
# => [image/gif]
-
1
def type_for(filename, platform = false)
-
12
ext = filename.chomp.downcase.gsub(/.*\./o, '')
-
12
list = @extension_index[ext]
-
12
list.delete_if { |e| not e.platform? } if platform
-
12
list
-
end
-
-
# A synonym for MIME::Types.type_for
-
1
def of(filename, platform = false)
-
type_for(filename, platform)
-
end
-
-
# Add one or more MIME::Type objects to the set of known types. Each
-
# type should be experimental (e.g., 'application/x-ruby'). If the type
-
# is already known, a warning will be displayed.
-
#
-
# <strong>Please inform the maintainer of this module when registered
-
# types are missing.</strong>
-
1
def add(*types)
-
1581
types.each do |mime_type|
-
3090
if mime_type.kind_of? MIME::Types
-
24
add(*mime_type.defined_types)
-
else
-
3066
if @type_variants.include?(mime_type.simplified)
-
68
if @type_variants[mime_type.simplified].include?(mime_type)
-
2
warn "Type #{mime_type} already registered as a variant of #{mime_type.simplified}." unless defined? MIME::Types::STARTUP
-
end
-
end
-
3066
add_type_variant(mime_type)
-
3066
index_extensions(mime_type)
-
end
-
end
-
end
-
-
1
class << self
-
1
def add_type_variant(mime_type) #:nodoc:
-
@__types__.add_type_variant(mime_type)
-
end
-
-
1
def index_extensions(mime_type) #:nodoc:
-
@__types__.index_extensions(mime_type)
-
end
-
-
# The regular expression used to match a file-based MIME type
-
# definition.
-
1
TEXT_FORMAT_RE = %r{
-
^
-
\s*
-
([*])? # 0: Unregistered?
-
(!)? # 1: Obsolete?
-
(?:(\w+):)? # 2: Platform marker
-
#{MIME::Type::MEDIA_TYPE_RE} # 3,4: Media type
-
(?:\s+@([^\s]+))? # 5: Extensions
-
(?:\s+:(#{MIME::Type::ENCODING_RE}))? # 6: Encoding
-
(?:\s+'(.+))? # 7: URL list
-
(?:\s+=(.+))? # 8: Documentation
-
\s*
-
$
-
}x
-
-
# Build the type list from a file in the format:
-
#
-
# [*][!][os:]mt/st[<ws>@ext][<ws>:enc][<ws>'url-list][<ws>=docs]
-
#
-
# == *
-
# An unofficial MIME type. This should be used if and only if the MIME type
-
# is not properly specified (that is, not under either x-type or
-
# vnd.name.type).
-
#
-
# == !
-
# An obsolete MIME type. May be used with an unofficial MIME type.
-
#
-
# == os:
-
# Platform-specific MIME type definition.
-
#
-
# == mt
-
# The media type.
-
#
-
# == st
-
# The media subtype.
-
#
-
# == <ws>@ext
-
# The list of comma-separated extensions.
-
#
-
# == <ws>:enc
-
# The encoding.
-
#
-
# == <ws>'url-list
-
# The list of comma-separated URLs.
-
#
-
# == <ws>=docs
-
# The documentation string.
-
#
-
# That is, everything except the media type and the subtype is optional. The
-
# more information that's available, though, the richer the values that can
-
# be provided.
-
1
def load_from_file(filename) #:nodoc:
-
24
if defined? ::Encoding
-
48
data = File.open(filename, 'r:UTF-8') { |f| f.read }
-
else
-
data = File.open(filename) { |f| f.read }
-
end
-
24
data = data.split($/)
-
24
mime = MIME::Types.new
-
24
data.each_with_index { |line, index|
-
1533
item = line.chomp.strip.gsub(%r{#.*}o, '')
-
1533
next if item.empty?
-
-
1533
begin
-
1533
m = TEXT_FORMAT_RE.match(item).captures
-
rescue Exception
-
puts "#{filename}:#{index}: Parsing error in MIME type definitions."
-
puts "=> #{line}"
-
raise
-
end
-
-
unregistered, obsolete, platform, mediatype, subtype, extensions,
-
1533
encoding, urls, docs = *m
-
-
1533
extensions &&= extensions.split(/,/)
-
1533
urls &&= urls.split(/,/)
-
-
1533
mime_type = MIME::Type.new("#{mediatype}/#{subtype}") do |t|
-
1533
t.extensions = extensions
-
1533
t.encoding = encoding
-
1533
t.system = platform
-
1533
t.obsolete = obsolete
-
1533
t.registered = false if unregistered
-
1533
t.docs = docs
-
1533
t.url = urls
-
end
-
-
1533
mime.add(mime_type)
-
}
-
24
mime
-
end
-
-
# Returns a list of MIME::Type objects, which may be empty. The
-
# optional flag parameters are :complete (finds only complete
-
# MIME::Type objects) and :platform (finds only MIME::Types for the
-
# current platform). It is possible for multiple matches to be
-
# returned for either type (in the example below, 'text/plain' returns
-
# two values -- one for the general case, and one for VMS systems.
-
#
-
# puts "\nMIME::Types['text/plain']"
-
# MIME::Types['text/plain'].each { |t| puts t.to_a.join(", ") }
-
#
-
# puts "\nMIME::Types[/^image/, :complete => true]"
-
# MIME::Types[/^image/, :complete => true].each do |t|
-
# puts t.to_a.join(", ")
-
# end
-
1
def [](type_id, flags = {})
-
2
@__types__[type_id, flags]
-
end
-
-
# Return the list of MIME::Types which belongs to the file based on
-
# its filename extension. If +platform+ is +true+, then only file
-
# types that are specific to the current platform will be returned.
-
#
-
# This will always return an array.
-
#
-
# puts "MIME::Types.type_for('citydesk.xml')
-
# => [application/xml, text/xml]
-
# puts "MIME::Types.type_for('citydesk.gif')
-
# => [image/gif]
-
1
def type_for(filename, platform = false)
-
12
@__types__.type_for(filename, platform)
-
end
-
-
# A synonym for MIME::Types.type_for
-
1
def of(filename, platform = false)
-
@__types__.type_for(filename, platform)
-
end
-
-
# Add one or more MIME::Type objects to the set of known types. Each
-
# type should be experimental (e.g., 'application/x-ruby'). If the
-
# type is already known, a warning will be displayed.
-
#
-
# <strong>Please inform the maintainer of this module when registered
-
# types are missing.</strong>
-
1
def add(*types)
-
24
@__types__.add(*types)
-
end
-
end
-
-
1
files = Dir[File.join(File.dirname(__FILE__), 'types', '*')]
-
1
MIME::Types::STARTUP = true unless $DEBUG
-
25
files.sort.each { |file| add load_from_file(file) }
-
1
remove_const :STARTUP if defined? STARTUP
-
end
-
end
-
-
# vim: ft=ruby
-
1
begin
-
1
require 'rubygems'
-
1
gem 'minitest'
-
rescue Gem::LoadError
-
# do nothing
-
end
-
-
1
require 'minitest/unit'
-
1
require 'minitest/spec'
-
1
require 'minitest/mock'
-
-
1
MiniTest::Unit.autorun
-
1
class MockExpectationError < StandardError # :nodoc:
-
end # omg... worst bug ever. rdoc doesn't allow 1-liners
-
-
##
-
# A simple and clean mock object framework.
-
-
1
module MiniTest
-
-
##
-
# All mock objects are an instance of Mock
-
-
1
class Mock
-
1
alias :__respond_to? :respond_to?
-
-
1
skip_methods = %w(object_id respond_to_missing? inspect === to_s)
-
-
1
instance_methods.each do |m|
-
102
undef_method m unless skip_methods.include?(m.to_s) || m =~ /^__/
-
end
-
-
1
def initialize # :nodoc:
-
@expected_calls = Hash.new { |calls, name| calls[name] = [] }
-
@actual_calls = Hash.new { |calls, name| calls[name] = [] }
-
end
-
-
##
-
# Expect that method +name+ is called, optionally with +args+, and returns
-
# +retval+.
-
#
-
# @mock.expect(:meaning_of_life, 42)
-
# @mock.meaning_of_life # => 42
-
#
-
# @mock.expect(:do_something_with, true, [some_obj, true])
-
# @mock.do_something_with(some_obj, true) # => true
-
#
-
# +args+ is compared to the expected args using case equality (ie, the
-
# '===' operator), allowing for less specific expectations.
-
#
-
# @mock.expect(:uses_any_string, true, [String])
-
# @mock.uses_any_string("foo") # => true
-
# @mock.verify # => true
-
#
-
# @mock.expect(:uses_one_string, true, ["foo"]
-
# @mock.uses_one_string("bar") # => true
-
# @mock.verify # => raises MockExpectationError
-
-
1
def expect(name, retval, args=[])
-
raise ArgumentError, "args must be an array" unless Array === args
-
@expected_calls[name] << { :retval => retval, :args => args }
-
self
-
end
-
-
1
def __call name, data # :nodoc:
-
case data
-
when Hash then
-
"#{name}(#{data[:args].inspect[1..-2]}) => #{data[:retval].inspect}"
-
else
-
data.map { |d| __call name, d }.join ", "
-
end
-
end
-
-
##
-
# Verify that all methods were called as expected. Raises
-
# +MockExpectationError+ if the mock object was not called as
-
# expected.
-
-
1
def verify
-
@expected_calls.each do |name, calls|
-
calls.each do |expected|
-
msg1 = "expected #{__call name, expected}"
-
msg2 = "#{msg1}, got [#{__call name, @actual_calls[name]}]"
-
-
raise MockExpectationError, msg2 if
-
@actual_calls.has_key?(name) and
-
not @actual_calls[name].include?(expected)
-
-
raise MockExpectationError, msg1 unless
-
@actual_calls.has_key?(name) and
-
@actual_calls[name].include?(expected)
-
end
-
end
-
true
-
end
-
-
1
def method_missing(sym, *args) # :nodoc:
-
unless @expected_calls.has_key?(sym) then
-
raise NoMethodError, "unmocked method %p, expected one of %p" %
-
[sym, @expected_calls.keys.sort_by(&:to_s)]
-
end
-
-
index = @actual_calls[sym].length
-
expected_call = @expected_calls[sym][index]
-
-
unless expected_call then
-
raise MockExpectationError, "No more expects available for %p: %p" %
-
[sym, args]
-
end
-
-
expected_args, retval = expected_call[:args], expected_call[:retval]
-
-
if expected_args.size != args.size then
-
raise ArgumentError, "mocked method %p expects %d arguments, got %d" %
-
[sym, expected_args.size, args.size]
-
end
-
-
fully_matched = expected_args.zip(args).all? { |mod, a|
-
mod === a or mod == a
-
}
-
-
unless fully_matched then
-
raise MockExpectationError, "mocked method %p called with unexpected arguments %p" %
-
[sym, args]
-
end
-
-
@actual_calls[sym] << {
-
:retval => retval,
-
:args => expected_args.zip(args).map { |mod, a| mod === a ? mod : a }
-
}
-
-
retval
-
end
-
-
1
def respond_to?(sym, include_private = false) # :nodoc:
-
return true if @expected_calls.has_key?(sym.to_sym)
-
return __respond_to?(sym, include_private)
-
end
-
end
-
end
-
-
1
class Object # :nodoc:
-
-
##
-
# Add a temporary stubbed method replacing +name+ for the duration
-
# of the +block+. If +val_or_callable+ responds to #call, then it
-
# returns the result of calling it, otherwise returns the value
-
# as-is. Cleans up the stub at the end of the +block+.
-
#
-
# def test_stale_eh
-
# obj_under_test = Something.new
-
# refute obj_under_test.stale?
-
#
-
# Time.stub :now, Time.at(0) do
-
# assert obj_under_test.stale?
-
# end
-
# end
-
-
1
def stub name, val_or_callable, &block
-
new_name = "__minitest_stub__#{name}"
-
-
metaclass = class << self; self; end
-
-
if respond_to? name and not methods.map(&:to_s).include? name.to_s then
-
metaclass.send :define_method, name do |*args|
-
super(*args)
-
end
-
end
-
-
metaclass.send :alias_method, new_name, name
-
-
metaclass.send :define_method, name do |*args|
-
if val_or_callable.respond_to? :call then
-
val_or_callable.call(*args)
-
else
-
val_or_callable
-
end
-
end
-
-
yield self
-
ensure
-
metaclass.send :undef_method, name
-
metaclass.send :alias_method, name, new_name
-
metaclass.send :undef_method, new_name
-
end
-
end
-
1
class ParallelEach
-
1
require 'thread'
-
1
include Enumerable
-
-
1
N = (ENV['N'] || 2).to_i
-
-
1
def initialize list
-
1
@queue = Queue.new # *sigh*... the Queue api sucks sooo much...
-
-
1
list.each { |i| @queue << i }
-
3
N.times { @queue << nil }
-
end
-
-
1
def grep pattern
-
self.class.new super
-
end
-
-
1
def each
-
1
threads = N.times.map {
-
2
Thread.new do
-
2
Thread.current.abort_on_exception = true
-
2
while job = @queue.pop
-
yield job
-
end
-
end
-
}
-
1
threads.map(&:join)
-
end
-
end
-
#!/usr/bin/ruby -w
-
-
1
require 'minitest/unit'
-
-
1
class Module # :nodoc:
-
1
def infect_an_assertion meth, new_name, dont_flip = false # :nodoc:
-
# warn "%-22p -> %p %p" % [meth, new_name, dont_flip]
-
29
self.class_eval <<-EOM
-
def #{new_name} *args
-
case
-
when Proc === self then
-
MiniTest::Spec.current.#{meth}(*args, &self)
-
when #{!!dont_flip} then
-
MiniTest::Spec.current.#{meth}(self, *args)
-
else
-
MiniTest::Spec.current.#{meth}(args.first, self, *args[1..-1])
-
end
-
end
-
EOM
-
end
-
-
##
-
# infect_with_assertions has been removed due to excessive clever.
-
# Use infect_an_assertion directly instead.
-
-
1
def infect_with_assertions(pos_prefix, neg_prefix,
-
skip_re,
-
dont_flip_re = /\c0/,
-
map = {})
-
abort "infect_with_assertions is dead. Use infect_an_assertion directly"
-
end
-
end
-
-
1
module Kernel # :nodoc:
-
##
-
# Describe a series of expectations for a given target +desc+.
-
#
-
# TODO: find good tutorial url.
-
#
-
# Defines a test class subclassing from either MiniTest::Spec or
-
# from the surrounding describe's class. The surrounding class may
-
# subclass MiniTest::Spec manually in order to easily share code:
-
#
-
# class MySpec < MiniTest::Spec
-
# # ... shared code ...
-
# end
-
#
-
# class TestStuff < MySpec
-
# it "does stuff" do
-
# # shared code available here
-
# end
-
# describe "inner stuff" do
-
# it "still does stuff" do
-
# # ...and here
-
# end
-
# end
-
# end
-
-
1
def describe desc, additional_desc = nil, &block # :doc:
-
26
stack = MiniTest::Spec.describe_stack
-
26
name = [stack.last, desc, additional_desc].compact.join("::")
-
26
sclas = stack.last || if Class === self && self < MiniTest::Spec then
-
self
-
else
-
18
MiniTest::Spec.spec_type desc
-
end
-
-
26
cls = sclas.create name, desc
-
-
26
stack.push cls
-
26
cls.class_eval(&block)
-
26
stack.pop
-
26
cls
-
end
-
1
private :describe
-
end
-
-
##
-
# MiniTest::Spec -- The faster, better, less-magical spec framework!
-
#
-
# For a list of expectations, see MiniTest::Expectations.
-
-
1
class MiniTest::Spec < MiniTest::Unit::TestCase
-
##
-
# Contains pairs of matchers and Spec classes to be used to
-
# calculate the superclass of a top-level describe. This allows for
-
# automatically customizable spec types.
-
#
-
# See: register_spec_type and spec_type
-
-
1
TYPES = [[//, MiniTest::Spec]]
-
-
##
-
# Register a new type of spec that matches the spec's description.
-
# This method can take either a Regexp and a spec class or a spec
-
# class and a block that takes the description and returns true if
-
# it matches.
-
#
-
# Eg:
-
#
-
# register_spec_type(/Controller$/, MiniTest::Spec::Rails)
-
#
-
# or:
-
#
-
# register_spec_type(MiniTest::Spec::RailsModel) do |desc|
-
# desc.superclass == ActiveRecord::Base
-
# end
-
-
1
def self.register_spec_type(*args, &block)
-
6
if block then
-
3
matcher, klass = block, args.first
-
else
-
3
matcher, klass = *args
-
end
-
6
TYPES.unshift [matcher, klass]
-
end
-
-
##
-
# Figure out the spec class to use based on a spec's description. Eg:
-
#
-
# spec_type("BlahController") # => MiniTest::Spec::Rails
-
-
1
def self.spec_type desc
-
31
TYPES.find { |matcher, klass|
-
140
if matcher.respond_to? :call then
-
42
matcher.call desc
-
else
-
98
matcher === desc.to_s
-
end
-
}.last
-
end
-
-
1
@@describe_stack = []
-
1
def self.describe_stack # :nodoc:
-
26
@@describe_stack
-
end
-
-
##
-
# Returns the children of this spec.
-
-
1
def self.children
-
133
@children ||= []
-
end
-
-
1
def self.nuke_test_methods! # :nodoc:
-
26
self.public_instance_methods.grep(/^test_/).each do |name|
-
self.send :undef_method, name
-
end
-
end
-
-
##
-
# Define a 'before' action. Inherits the way normal methods should.
-
#
-
# NOTE: +type+ is ignored and is only there to make porting easier.
-
#
-
# Equivalent to MiniTest::Unit::TestCase#setup.
-
-
1
def self.before type = nil, &block
-
define_method :setup do
-
super()
-
self.instance_eval(&block)
-
end
-
end
-
-
##
-
# Define an 'after' action. Inherits the way normal methods should.
-
#
-
# NOTE: +type+ is ignored and is only there to make porting easier.
-
#
-
# Equivalent to MiniTest::Unit::TestCase#teardown.
-
-
1
def self.after type = nil, &block
-
define_method :teardown do
-
self.instance_eval(&block)
-
super()
-
end
-
end
-
-
##
-
# Define an expectation with name +desc+. Name gets morphed to a
-
# proper test method name. For some freakish reason, people who
-
# write specs don't like class inheritence, so this goes way out of
-
# its way to make sure that expectations aren't inherited.
-
#
-
# This is also aliased to #specify and doesn't require a +desc+ arg.
-
#
-
# Hint: If you _do_ want inheritence, use minitest/unit. You can mix
-
# and match between assertions and expectations as much as you want.
-
-
1
def self.it desc = "anonymous", &block
-
107
block ||= proc { skip "(no tests defined)" }
-
-
107
@specs ||= 0
-
107
@specs += 1
-
-
107
name = "test_%04d_%s" % [ @specs, desc ]
-
-
107
define_method name, &block
-
-
107
self.children.each do |mod|
-
mod.send :undef_method, name if mod.public_method_defined? name
-
end
-
-
107
name
-
end
-
-
##
-
# Essentially, define an accessor for +name+ with +block+.
-
#
-
# Why use let instead of def? I honestly don't know.
-
-
1
def self.let name, &block
-
define_method name do
-
@_memoized ||= {}
-
@_memoized.fetch(name) { |k| @_memoized[k] = instance_eval(&block) }
-
end
-
end
-
-
##
-
# Another lazy man's accessor generator. Made even more lazy by
-
# setting the name for you to +subject+.
-
-
1
def self.subject &block
-
let :subject, &block
-
end
-
-
1
def self.create name, desc # :nodoc:
-
26
cls = Class.new(self) do
-
26
@name = name
-
26
@desc = desc
-
-
26
nuke_test_methods!
-
end
-
-
26
children << cls
-
-
26
cls
-
end
-
-
1
def self.to_s # :nodoc:
-
363
defined?(@name) ? @name : super
-
end
-
-
# :stopdoc:
-
1
class << self
-
1
attr_reader :desc
-
1
alias :specify :it
-
1
alias :name :to_s
-
end
-
# :startdoc:
-
end
-
-
##
-
# It's where you hide your "assertions".
-
-
1
module MiniTest::Expectations
-
##
-
# See MiniTest::Assertions#assert_empty.
-
#
-
# collection.must_be_empty
-
#
-
# :method: must_be_empty
-
-
1
infect_an_assertion :assert_empty, :must_be_empty, :unary
-
-
##
-
# See MiniTest::Assertions#assert_equal
-
#
-
# a.must_equal b
-
#
-
# :method: must_equal
-
-
1
infect_an_assertion :assert_equal, :must_equal
-
-
##
-
# See MiniTest::Assertions#assert_in_delta
-
#
-
# n.must_be_close_to m [, delta]
-
#
-
# :method: must_be_close_to
-
-
1
infect_an_assertion :assert_in_delta, :must_be_close_to
-
-
1
alias :must_be_within_delta :must_be_close_to # :nodoc:
-
-
##
-
# See MiniTest::Assertions#assert_in_epsilon
-
#
-
# n.must_be_within_epsilon m [, epsilon]
-
#
-
# :method: must_be_within_epsilon
-
-
1
infect_an_assertion :assert_in_epsilon, :must_be_within_epsilon
-
-
##
-
# See MiniTest::Assertions#assert_includes
-
#
-
# collection.must_include obj
-
#
-
# :method: must_include
-
-
1
infect_an_assertion :assert_includes, :must_include, :reverse
-
-
##
-
# See MiniTest::Assertions#assert_instance_of
-
#
-
# obj.must_be_instance_of klass
-
#
-
# :method: must_be_instance_of
-
-
1
infect_an_assertion :assert_instance_of, :must_be_instance_of
-
-
##
-
# See MiniTest::Assertions#assert_kind_of
-
#
-
# obj.must_be_kind_of mod
-
#
-
# :method: must_be_kind_of
-
-
1
infect_an_assertion :assert_kind_of, :must_be_kind_of
-
-
##
-
# See MiniTest::Assertions#assert_match
-
#
-
# a.must_match b
-
#
-
# :method: must_match
-
-
1
infect_an_assertion :assert_match, :must_match
-
-
##
-
# See MiniTest::Assertions#assert_nil
-
#
-
# obj.must_be_nil
-
#
-
# :method: must_be_nil
-
-
1
infect_an_assertion :assert_nil, :must_be_nil, :unary
-
-
##
-
# See MiniTest::Assertions#assert_operator
-
#
-
# n.must_be :<=, 42
-
#
-
# This can also do predicates:
-
#
-
# str.must_be :empty?
-
#
-
# :method: must_be
-
-
1
infect_an_assertion :assert_operator, :must_be, :reverse
-
-
##
-
# See MiniTest::Assertions#assert_output
-
#
-
# proc { ... }.must_output out_or_nil [, err]
-
#
-
# :method: must_output
-
-
1
infect_an_assertion :assert_output, :must_output
-
-
##
-
# See MiniTest::Assertions#assert_raises
-
#
-
# proc { ... }.must_raise exception
-
#
-
# :method: must_raise
-
-
1
infect_an_assertion :assert_raises, :must_raise
-
-
##
-
# See MiniTest::Assertions#assert_respond_to
-
#
-
# obj.must_respond_to msg
-
#
-
# :method: must_respond_to
-
-
1
infect_an_assertion :assert_respond_to, :must_respond_to, :reverse
-
-
##
-
# See MiniTest::Assertions#assert_same
-
#
-
# a.must_be_same_as b
-
#
-
# :method: must_be_same_as
-
-
1
infect_an_assertion :assert_same, :must_be_same_as
-
-
##
-
# See MiniTest::Assertions#assert_send
-
# TODO: remove me
-
#
-
# a.must_send
-
#
-
# :method: must_send
-
-
1
infect_an_assertion :assert_send, :must_send
-
-
##
-
# See MiniTest::Assertions#assert_silent
-
#
-
# proc { ... }.must_be_silent
-
#
-
# :method: must_be_silent
-
-
1
infect_an_assertion :assert_silent, :must_be_silent
-
-
##
-
# See MiniTest::Assertions#assert_throws
-
#
-
# proc { ... }.must_throw sym
-
#
-
# :method: must_throw
-
-
1
infect_an_assertion :assert_throws, :must_throw
-
-
##
-
# See MiniTest::Assertions#refute_empty
-
#
-
# collection.wont_be_empty
-
#
-
# :method: wont_be_empty
-
-
1
infect_an_assertion :refute_empty, :wont_be_empty, :unary
-
-
##
-
# See MiniTest::Assertions#refute_equal
-
#
-
# a.wont_equal b
-
#
-
# :method: wont_equal
-
-
1
infect_an_assertion :refute_equal, :wont_equal
-
-
##
-
# See MiniTest::Assertions#refute_in_delta
-
#
-
# n.wont_be_close_to m [, delta]
-
#
-
# :method: wont_be_close_to
-
-
1
infect_an_assertion :refute_in_delta, :wont_be_close_to
-
-
1
alias :wont_be_within_delta :wont_be_close_to # :nodoc:
-
-
##
-
# See MiniTest::Assertions#refute_in_epsilon
-
#
-
# n.wont_be_within_epsilon m [, epsilon]
-
#
-
# :method: wont_be_within_epsilon
-
-
1
infect_an_assertion :refute_in_epsilon, :wont_be_within_epsilon
-
-
##
-
# See MiniTest::Assertions#refute_includes
-
#
-
# collection.wont_include obj
-
#
-
# :method: wont_include
-
-
1
infect_an_assertion :refute_includes, :wont_include, :reverse
-
-
##
-
# See MiniTest::Assertions#refute_instance_of
-
#
-
# obj.wont_be_instance_of klass
-
#
-
# :method: wont_be_instance_of
-
-
1
infect_an_assertion :refute_instance_of, :wont_be_instance_of
-
-
##
-
# See MiniTest::Assertions#refute_kind_of
-
#
-
# obj.wont_be_kind_of mod
-
#
-
# :method: wont_be_kind_of
-
-
1
infect_an_assertion :refute_kind_of, :wont_be_kind_of
-
-
##
-
# See MiniTest::Assertions#refute_match
-
#
-
# a.wont_match b
-
#
-
# :method: wont_match
-
-
1
infect_an_assertion :refute_match, :wont_match
-
-
##
-
# See MiniTest::Assertions#refute_nil
-
#
-
# obj.wont_be_nil
-
#
-
# :method: wont_be_nil
-
-
1
infect_an_assertion :refute_nil, :wont_be_nil, :unary
-
-
##
-
# See MiniTest::Assertions#refute_operator
-
#
-
# n.wont_be :<=, 42
-
#
-
# This can also do predicates:
-
#
-
# str.wont_be :empty?
-
#
-
# :method: wont_be
-
-
1
infect_an_assertion :refute_operator, :wont_be, :reverse
-
-
##
-
# See MiniTest::Assertions#refute_respond_to
-
#
-
# obj.wont_respond_to msg
-
#
-
# :method: wont_respond_to
-
-
1
infect_an_assertion :refute_respond_to, :wont_respond_to, :reverse
-
-
##
-
# See MiniTest::Assertions#refute_same
-
#
-
# a.wont_be_same_as b
-
#
-
# :method: wont_be_same_as
-
-
1
infect_an_assertion :refute_same, :wont_be_same_as
-
end
-
-
1
class Object # :nodoc:
-
1
include MiniTest::Expectations
-
end
-
1
require 'optparse'
-
1
require 'rbconfig'
-
1
require 'thread' # required for 1.8
-
1
require 'minitest/parallel_each'
-
-
##
-
# Minimal (mostly drop-in) replacement for test-unit.
-
#
-
# :include: README.txt
-
-
1
module MiniTest
-
-
1
def self.const_missing name # :nodoc:
-
case name
-
when :MINI_DIR then
-
msg = "MiniTest::MINI_DIR was removed. Don't violate other's internals."
-
warn "WAR\NING: #{msg}"
-
warn "WAR\NING: Used by #{caller.first}."
-
const_set :MINI_DIR, "bad value"
-
else
-
super
-
end
-
end
-
-
##
-
# Assertion base class
-
-
1
class Assertion < Exception; end
-
-
##
-
# Assertion raised when skipping a test
-
-
1
class Skip < Assertion; end
-
-
1
class << self
-
1
attr_accessor :backtrace_filter
-
end
-
-
1
class BacktraceFilter # :nodoc:
-
1
def filter bt
-
9
return ["No backtrace"] unless bt
-
-
9
new_bt = []
-
-
9
unless $DEBUG then
-
9
bt.each do |line|
-
101
break if line =~ /lib\/minitest/
-
92
new_bt << line
-
end
-
-
70
new_bt = bt.reject { |line| line =~ /lib\/minitest/ } if new_bt.empty?
-
9
new_bt = bt.dup if new_bt.empty?
-
else
-
new_bt = bt.dup
-
end
-
-
9
new_bt
-
end
-
end
-
-
1
self.backtrace_filter = BacktraceFilter.new
-
-
1
def self.filter_backtrace bt # :nodoc:
-
9
backtrace_filter.filter bt
-
end
-
-
##
-
# MiniTest Assertions. All assertion methods accept a +msg+ which is
-
# printed if the assertion fails.
-
-
1
module Assertions
-
1
UNDEFINED = Object.new # :nodoc:
-
-
1
def UNDEFINED.inspect # :nodoc:
-
"UNDEFINED" # again with the rdoc bugs... :(
-
end
-
-
##
-
# Returns the diff command to use in #diff. Tries to intelligently
-
# figure out what diff to use.
-
-
1
def self.diff
-
@diff = if RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ then
-
"diff.exe -u"
-
else
-
1
if system("gdiff", __FILE__, __FILE__)
-
"gdiff -u" # solaris and kin suck
-
elsif system("diff", __FILE__, __FILE__)
-
1
"diff -u"
-
else
-
nil
-
end
-
3
end unless defined? @diff
-
-
3
@diff
-
end
-
-
##
-
# Set the diff command to use in #diff.
-
-
1
def self.diff= o
-
@diff = o
-
end
-
-
##
-
# Returns a diff between +exp+ and +act+. If there is no known
-
# diff command or if it doesn't make sense to diff the output
-
# (single line, short output), then it simply returns a basic
-
# comparison between the two.
-
-
1
def diff exp, act
-
3
require "tempfile"
-
-
3
expect = mu_pp_for_diff exp
-
3
butwas = mu_pp_for_diff act
-
3
result = nil
-
-
3
need_to_diff =
-
MiniTest::Assertions.diff &&
-
(expect.include?("\n") ||
-
3
butwas.include?("\n") ||
-
expect.size > 30 ||
-
butwas.size > 30 ||
-
expect == butwas)
-
-
return "Expected: #{mu_pp exp}\n Actual: #{mu_pp act}" unless
-
3
need_to_diff
-
-
Tempfile.open("expect") do |a|
-
a.puts expect
-
a.flush
-
-
Tempfile.open("butwas") do |b|
-
b.puts butwas
-
b.flush
-
-
result = `#{MiniTest::Assertions.diff} #{a.path} #{b.path}`
-
result.sub!(/^\-\-\- .+/, "--- expected")
-
result.sub!(/^\+\+\+ .+/, "+++ actual")
-
-
if result.empty? then
-
klass = exp.class
-
result = [
-
"No visible difference in the #{klass}#inspect output.",
-
"You should look at your implementation of #{klass}#==.",
-
expect
-
].join "\n"
-
end
-
end
-
end
-
-
result
-
end
-
-
##
-
# This returns a human-readable version of +obj+. By default
-
# #inspect is called. You can override this to use #pretty_print
-
# if you want.
-
-
1
def mu_pp obj
-
21
s = obj.inspect
-
21
s = s.encode Encoding.default_external if defined? Encoding
-
21
s
-
end
-
-
##
-
# This returns a diff-able human-readable version of +obj+. This
-
# differs from the regular mu_pp because it expands escaped
-
# newlines and makes hex-values generic (like object_ids). This
-
# uses mu_pp to do the first pass and then cleans it up.
-
-
1
def mu_pp_for_diff obj # TODO: possibly rename
-
6
mu_pp(obj).gsub(/\\n/, "\n").gsub(/0x[a-f0-9]+/m, '0xXXXXXX')
-
end
-
-
1
def _assertions= n # :nodoc:
-
455
@_assertions = n
-
end
-
-
1
def _assertions # :nodoc:
-
601
@_assertions ||= 0
-
end
-
-
##
-
# Fails unless +test+ is a true value.
-
-
1
def assert test, msg = nil
-
309
msg ||= "Failed assertion, no message given."
-
309
self._assertions += 1
-
309
unless test then
-
3
msg = msg.call if Proc === msg
-
3
raise MiniTest::Assertion, msg
-
end
-
306
true
-
end
-
-
##
-
# Fails unless the block returns a true value.
-
#
-
# NOTE: This method is deprecated, use assert. It will be removed
-
# on 2013-01-01."
-
-
1
def assert_block msg = nil
-
warn "NOTE: MiniTest::Unit::TestCase#assert_block is deprecated, use assert. It will be removed on 2013-01-01. Called from #{caller.first}"
-
msg = message(msg) { "Expected block to return true value" }
-
assert yield, msg
-
end
-
-
##
-
# Fails unless +obj+ is empty.
-
-
1
def assert_empty obj, msg = nil
-
msg = message(msg) { "Expected #{mu_pp(obj)} to be empty" }
-
assert_respond_to obj, :empty?
-
assert obj.empty?, msg
-
end
-
-
##
-
# Fails unless <tt>exp == act</tt> printing the difference between
-
# the two, if possible.
-
#
-
# If there is no visible difference but the assertion fails, you
-
# should suspect that your #== is buggy, or your inspect output is
-
# missing crucial details.
-
#
-
# For floats use assert_in_delta.
-
#
-
# See also: MiniTest::Assertions.diff
-
-
1
def assert_equal exp, act, msg = nil
-
258
msg = message(msg, "") { diff exp, act }
-
255
assert(exp == act, msg)
-
end
-
-
##
-
# For comparing Floats. Fails unless +exp+ and +act+ are within +delta+
-
# of each other.
-
#
-
# assert_in_delta Math::PI, (22.0 / 7.0), 0.01
-
-
1
def assert_in_delta exp, act, delta = 0.001, msg = nil
-
n = (exp - act).abs
-
msg = message(msg) { "Expected |#{exp} - #{act}| (#{n}) to be < #{delta}"}
-
assert delta >= n, msg
-
end
-
-
##
-
# For comparing Floats. Fails unless +exp+ and +act+ have a relative
-
# error less than +epsilon+.
-
-
1
def assert_in_epsilon a, b, epsilon = 0.001, msg = nil
-
assert_in_delta a, b, [a.abs, b.abs].min * epsilon, msg
-
end
-
-
##
-
# Fails unless +collection+ includes +obj+.
-
-
1
def assert_includes collection, obj, msg = nil
-
msg = message(msg) {
-
"Expected #{mu_pp(collection)} to include #{mu_pp(obj)}"
-
}
-
assert_respond_to collection, :include?
-
assert collection.include?(obj), msg
-
end
-
-
##
-
# Fails unless +obj+ is an instance of +cls+.
-
-
1
def assert_instance_of cls, obj, msg = nil
-
3
msg = message(msg) {
-
"Expected #{mu_pp(obj)} to be an instance of #{cls}, not #{obj.class}"
-
}
-
-
3
assert obj.instance_of?(cls), msg
-
end
-
-
##
-
# Fails unless +obj+ is a kind of +cls+.
-
-
1
def assert_kind_of cls, obj, msg = nil # TODO: merge with instance_of
-
2
msg = message(msg) {
-
"Expected #{mu_pp(obj)} to be a kind of #{cls}, not #{obj.class}" }
-
-
2
assert obj.kind_of?(cls), msg
-
end
-
-
##
-
# Fails unless +matcher+ <tt>=~</tt> +obj+.
-
-
1
def assert_match matcher, obj, msg = nil
-
12
msg = message(msg) { "Expected #{mu_pp matcher} to match #{mu_pp obj}" }
-
12
assert_respond_to matcher, :"=~"
-
12
matcher = Regexp.new Regexp.escape matcher if String === matcher
-
12
assert matcher =~ obj, msg
-
end
-
-
##
-
# Fails unless +obj+ is nil
-
-
1
def assert_nil obj, msg = nil
-
msg = message(msg) { "Expected #{mu_pp(obj)} to be nil" }
-
assert obj.nil?, msg
-
end
-
-
##
-
# For testing with binary operators.
-
#
-
# assert_operator 5, :<=, 4
-
-
1
def assert_operator o1, op, o2 = UNDEFINED, msg = nil
-
return assert_predicate o1, op, msg if UNDEFINED == o2
-
msg = message(msg) { "Expected #{mu_pp(o1)} to be #{op} #{mu_pp(o2)}" }
-
assert o1.__send__(op, o2), msg
-
end
-
-
##
-
# Fails if stdout or stderr do not output the expected results.
-
# Pass in nil if you don't care about that streams output. Pass in
-
# "" if you require it to be silent. Pass in a regexp if you want
-
# to pattern match.
-
#
-
# NOTE: this uses #capture_io, not #capture_subprocess_io.
-
#
-
# See also: #assert_silent
-
-
1
def assert_output stdout = nil, stderr = nil
-
out, err = capture_io do
-
yield
-
end
-
-
err_msg = Regexp === stderr ? :assert_match : :assert_equal if stderr
-
out_msg = Regexp === stdout ? :assert_match : :assert_equal if stdout
-
-
y = send err_msg, stderr, err, "In stderr" if err_msg
-
x = send out_msg, stdout, out, "In stdout" if out_msg
-
-
(!stdout || x) && (!stderr || y)
-
end
-
-
##
-
# For testing with predicates.
-
#
-
# assert_predicate str, :empty?
-
#
-
# This is really meant for specs and is front-ended by assert_operator:
-
#
-
# str.must_be :empty?
-
-
1
def assert_predicate o1, op, msg = nil
-
msg = message(msg) { "Expected #{mu_pp(o1)} to be #{op}" }
-
assert o1.__send__(op), msg
-
end
-
-
##
-
# Fails unless the block raises one of +exp+. Returns the
-
# exception matched so you can check the message, attributes, etc.
-
-
1
def assert_raises *exp
-
9
msg = "#{exp.pop}.\n" if String === exp.last
-
-
9
should_raise = false
-
9
begin
-
9
yield
-
should_raise = true
-
rescue MiniTest::Skip => e
-
details = "#{msg}#{mu_pp(exp)} exception expected, not"
-
-
if exp.include? MiniTest::Skip then
-
return e
-
else
-
raise e
-
end
-
rescue Exception => e
-
9
details = "#{msg}#{mu_pp(exp)} exception expected, not"
-
assert(exp.any? { |ex|
-
9
ex.instance_of?(Module) ? e.kind_of?(ex) : ex == e.class
-
9
}, exception_details(e, details))
-
-
9
return e
-
end
-
-
exp = exp.first if exp.size == 1
-
flunk "#{msg}#{mu_pp(exp)} expected but nothing was raised." if
-
should_raise
-
end
-
-
##
-
# Fails unless +obj+ responds to +meth+.
-
-
1
def assert_respond_to obj, meth, msg = nil
-
16
msg = message(msg) {
-
"Expected #{mu_pp(obj)} (#{obj.class}) to respond to ##{meth}"
-
}
-
16
assert obj.respond_to?(meth), msg
-
end
-
-
##
-
# Fails unless +exp+ and +act+ are #equal?
-
-
1
def assert_same exp, act, msg = nil
-
msg = message(msg) {
-
data = [mu_pp(act), act.object_id, mu_pp(exp), exp.object_id]
-
"Expected %s (oid=%d) to be the same as %s (oid=%d)" % data
-
}
-
assert exp.equal?(act), msg
-
end
-
-
##
-
# +send_ary+ is a receiver, message and arguments.
-
#
-
# Fails unless the call returns a true value
-
# TODO: I should prolly remove this from specs
-
-
1
def assert_send send_ary, m = nil
-
recv, msg, *args = send_ary
-
m = message(m) {
-
"Expected #{mu_pp(recv)}.#{msg}(*#{mu_pp(args)}) to return true" }
-
assert recv.__send__(msg, *args), m
-
end
-
-
##
-
# Fails if the block outputs anything to stderr or stdout.
-
#
-
# See also: #assert_output
-
-
1
def assert_silent
-
assert_output "", "" do
-
yield
-
end
-
end
-
-
##
-
# Fails unless the block throws +sym+
-
-
1
def assert_throws sym, msg = nil
-
default = "Expected #{mu_pp(sym)} to have been thrown"
-
caught = true
-
catch(sym) do
-
begin
-
yield
-
rescue ThreadError => e # wtf?!? 1.8 + threads == suck
-
default += ", not :#{e.message[/uncaught throw \`(\w+?)\'/, 1]}"
-
rescue ArgumentError => e # 1.9 exception
-
default += ", not #{e.message.split(/ /).last}"
-
rescue NameError => e # 1.8 exception
-
default += ", not #{e.name.inspect}"
-
end
-
caught = false
-
end
-
-
assert caught, message(msg) { default }
-
end
-
-
##
-
# Captures $stdout and $stderr into strings:
-
#
-
# out, err = capture_io do
-
# puts "Some info"
-
# warn "You did a bad thing"
-
# end
-
#
-
# assert_match %r%info%, out
-
# assert_match %r%bad%, err
-
#
-
# NOTE: For efficiency, this method uses StringIO and does not
-
# capture IO for subprocesses. Use #capture_subprocess_io for
-
# that.
-
-
1
def capture_io
-
require 'stringio'
-
-
captured_stdout, captured_stderr = StringIO.new, StringIO.new
-
-
synchronize do
-
orig_stdout, orig_stderr = $stdout, $stderr
-
$stdout, $stderr = captured_stdout, captured_stderr
-
-
begin
-
yield
-
ensure
-
$stdout = orig_stdout
-
$stderr = orig_stderr
-
end
-
end
-
-
return captured_stdout.string, captured_stderr.string
-
end
-
-
##
-
# Captures $stdout and $stderr into strings, using Tempfile to
-
# ensure that subprocess IO is captured as well.
-
#
-
# out, err = capture_subprocess_io do
-
# system "echo Some info"
-
# system "echo You did a bad thing 1>&2"
-
# end
-
#
-
# assert_match %r%info%, out
-
# assert_match %r%bad%, err
-
#
-
# NOTE: This method is approximately 10x slower than #capture_io so
-
# only use it when you need to test the output of a subprocess.
-
-
1
def capture_subprocess_io
-
require 'tempfile'
-
-
captured_stdout, captured_stderr = Tempfile.new("out"), Tempfile.new("err")
-
-
synchronize do
-
orig_stdout, orig_stderr = $stdout.dup, $stderr.dup
-
$stdout.reopen captured_stdout
-
$stderr.reopen captured_stderr
-
-
begin
-
yield
-
-
$stdout.rewind
-
$stderr.rewind
-
-
[captured_stdout.read, captured_stderr.read]
-
ensure
-
captured_stdout.unlink
-
captured_stderr.unlink
-
$stdout.reopen orig_stdout
-
$stderr.reopen orig_stderr
-
end
-
end
-
end
-
-
##
-
# Returns details for exception +e+
-
-
1
def exception_details e, msg
-
[
-
9
"#{msg}",
-
"Class: <#{e.class}>",
-
"Message: <#{e.message.inspect}>",
-
"---Backtrace---",
-
"#{MiniTest::filter_backtrace(e.backtrace).join("\n")}",
-
"---------------",
-
].join "\n"
-
end
-
-
##
-
# Fails with +msg+
-
-
1
def flunk msg = nil
-
msg ||= "Epic Fail!"
-
assert false, msg
-
end
-
-
##
-
# Returns a proc that will output +msg+ along with the default message.
-
-
1
def message msg = nil, ending = ".", &default
-
296
proc {
-
3
custom_message = "#{msg}.\n" unless msg.nil? or msg.to_s.empty?
-
3
"#{custom_message}#{default.call}#{ending}"
-
}
-
end
-
-
##
-
# used for counting assertions
-
-
1
def pass msg = nil
-
assert true
-
end
-
-
##
-
# Fails if +test+ is a true value
-
-
1
def refute test, msg = nil
-
8
msg ||= "Failed refutation, no message given"
-
8
not assert(! test, msg)
-
end
-
-
##
-
# Fails if +obj+ is empty.
-
-
1
def refute_empty obj, msg = nil
-
msg = message(msg) { "Expected #{mu_pp(obj)} to not be empty" }
-
assert_respond_to obj, :empty?
-
refute obj.empty?, msg
-
end
-
-
##
-
# Fails if <tt>exp == act</tt>.
-
#
-
# For floats use refute_in_delta.
-
-
1
def refute_equal exp, act, msg = nil
-
5
msg = message(msg) {
-
"Expected #{mu_pp(act)} to not be equal to #{mu_pp(exp)}"
-
}
-
5
refute exp == act, msg
-
end
-
-
##
-
# For comparing Floats. Fails if +exp+ is within +delta+ of +act+.
-
#
-
# refute_in_delta Math::PI, (22.0 / 7.0)
-
-
1
def refute_in_delta exp, act, delta = 0.001, msg = nil
-
n = (exp - act).abs
-
msg = message(msg) {
-
"Expected |#{exp} - #{act}| (#{n}) to not be < #{delta}"
-
}
-
refute delta > n, msg
-
end
-
-
##
-
# For comparing Floats. Fails if +exp+ and +act+ have a relative error
-
# less than +epsilon+.
-
-
1
def refute_in_epsilon a, b, epsilon = 0.001, msg = nil
-
refute_in_delta a, b, a * epsilon, msg
-
end
-
-
##
-
# Fails if +collection+ includes +obj+.
-
-
1
def refute_includes collection, obj, msg = nil
-
msg = message(msg) {
-
"Expected #{mu_pp(collection)} to not include #{mu_pp(obj)}"
-
}
-
assert_respond_to collection, :include?
-
refute collection.include?(obj), msg
-
end
-
-
##
-
# Fails if +obj+ is an instance of +cls+.
-
-
1
def refute_instance_of cls, obj, msg = nil
-
msg = message(msg) {
-
"Expected #{mu_pp(obj)} to not be an instance of #{cls}"
-
}
-
refute obj.instance_of?(cls), msg
-
end
-
-
##
-
# Fails if +obj+ is a kind of +cls+.
-
-
1
def refute_kind_of cls, obj, msg = nil # TODO: merge with instance_of
-
msg = message(msg) { "Expected #{mu_pp(obj)} to not be a kind of #{cls}" }
-
refute obj.kind_of?(cls), msg
-
end
-
-
##
-
# Fails if +matcher+ <tt>=~</tt> +obj+.
-
-
1
def refute_match matcher, obj, msg = nil
-
msg = message(msg) {"Expected #{mu_pp matcher} to not match #{mu_pp obj}"}
-
assert_respond_to matcher, :"=~"
-
matcher = Regexp.new Regexp.escape matcher if String === matcher
-
refute matcher =~ obj, msg
-
end
-
-
##
-
# Fails if +obj+ is nil.
-
-
1
def refute_nil obj, msg = nil
-
3
msg = message(msg) { "Expected #{mu_pp(obj)} to not be nil" }
-
3
refute obj.nil?, msg
-
end
-
-
##
-
# Fails if +o1+ is not +op+ +o2+. Eg:
-
#
-
# refute_operator 1, :>, 2 #=> pass
-
# refute_operator 1, :<, 2 #=> fail
-
-
1
def refute_operator o1, op, o2 = UNDEFINED, msg = nil
-
return refute_predicate o1, op, msg if UNDEFINED == o2
-
msg = message(msg) { "Expected #{mu_pp(o1)} to not be #{op} #{mu_pp(o2)}"}
-
refute o1.__send__(op, o2), msg
-
end
-
-
##
-
# For testing with predicates.
-
#
-
# refute_predicate str, :empty?
-
#
-
# This is really meant for specs and is front-ended by refute_operator:
-
#
-
# str.wont_be :empty?
-
-
1
def refute_predicate o1, op, msg = nil
-
msg = message(msg) { "Expected #{mu_pp(o1)} to not be #{op}" }
-
refute o1.__send__(op), msg
-
end
-
-
##
-
# Fails if +obj+ responds to the message +meth+.
-
-
1
def refute_respond_to obj, meth, msg = nil
-
msg = message(msg) { "Expected #{mu_pp(obj)} to not respond to #{meth}" }
-
-
refute obj.respond_to?(meth), msg
-
end
-
-
##
-
# Fails if +exp+ is the same (by object identity) as +act+.
-
-
1
def refute_same exp, act, msg = nil
-
msg = message(msg) {
-
data = [mu_pp(act), act.object_id, mu_pp(exp), exp.object_id]
-
"Expected %s (oid=%d) to not be the same as %s (oid=%d)" % data
-
}
-
refute exp.equal?(act), msg
-
end
-
-
##
-
# Skips the current test. Gets listed at the end of the run but
-
# doesn't cause a failure exit code.
-
-
1
def skip msg = nil, bt = caller
-
msg ||= "Skipped, no message given"
-
raise MiniTest::Skip, msg, bt
-
end
-
-
##
-
# Takes a block and wraps it with the runner's shared mutex.
-
-
1
def synchronize
-
Minitest::Unit.runner.synchronize do
-
yield
-
end
-
end
-
end
-
-
1
class Unit # :nodoc:
-
1
VERSION = "4.2.0" # :nodoc:
-
-
1
attr_accessor :report, :failures, :errors, :skips # :nodoc:
-
1
attr_accessor :test_count, :assertion_count # :nodoc:
-
1
attr_accessor :start_time # :nodoc:
-
1
attr_accessor :help # :nodoc:
-
1
attr_accessor :verbose # :nodoc:
-
1
attr_writer :options # :nodoc:
-
-
##
-
# Lazy accessor for options.
-
-
1
def options
-
47
@options ||= {}
-
end
-
-
1
@@installed_at_exit ||= false
-
1
@@out = $stdout
-
1
@@after_tests = []
-
-
##
-
# A simple hook allowing you to run a block of code after _all_ of
-
# the tests are done. Eg:
-
#
-
# MiniTest::Unit.after_tests { p $debugging_info }
-
-
1
def self.after_tests &block
-
@@after_tests << block
-
end
-
-
##
-
# Registers MiniTest::Unit to run tests at process exit
-
-
1
def self.autorun
-
at_exit {
-
1
next if $! # don't run if there was an exception
-
-
# the order here is important. The at_exit handler must be
-
# installed before anyone else gets a chance to install their
-
# own, that way we can be assured that our exit will be last
-
# to run (at_exit stacks).
-
1
exit_code = nil
-
-
1
at_exit {
-
1
@@after_tests.reverse_each(&:call)
-
1
exit false if exit_code && exit_code != 0
-
}
-
-
1
exit_code = MiniTest::Unit.new.run ARGV
-
1
} unless @@installed_at_exit
-
1
@@installed_at_exit = true
-
end
-
-
##
-
# Returns the stream to use for output.
-
-
1
def self.output
-
159
@@out
-
end
-
-
##
-
# Sets MiniTest::Unit to write output to +stream+. $stdout is the default
-
# output
-
-
1
def self.output= stream
-
@@out = stream
-
end
-
-
##
-
# Tells MiniTest::Unit to delegate to +runner+, an instance of a
-
# MiniTest::Unit subclass, when MiniTest::Unit#run is called.
-
-
1
def self.runner= runner
-
@@runner = runner
-
end
-
-
##
-
# Returns the MiniTest::Unit subclass instance that will be used
-
# to run the tests. A MiniTest::Unit instance is the default
-
# runner.
-
-
1
def self.runner
-
1
@@runner ||= self.new
-
end
-
-
##
-
# Return all plugins' run methods (methods that start with "run_").
-
-
1
def self.plugins
-
@@plugins ||= (["run_tests"] +
-
public_instance_methods(false).
-
2
grep(/^run_/).map { |s| s.to_s }).uniq
-
end
-
-
##
-
# Return the IO for output.
-
-
1
def output
-
159
self.class.output
-
end
-
-
1
def puts *a # :nodoc:
-
8
output.puts(*a)
-
end
-
-
1
def print *a # :nodoc:
-
146
output.print(*a)
-
end
-
-
##
-
# Runner for a given +type+ (eg, test vs bench).
-
-
1
def _run_anything type
-
1
suites = TestCase.send "#{type}_suites"
-
1
return if suites.empty?
-
-
1
start = Time.now
-
-
1
puts
-
1
puts "# Running #{type}s:"
-
1
puts
-
-
1
@test_count, @assertion_count = 0, 0
-
1
sync = output.respond_to? :"sync=" # stupid emacs
-
1
old_sync, output.sync = output.sync, true if sync
-
-
1
results = _run_suites suites, type
-
-
48
@test_count = results.inject(0) { |sum, (tc, _)| sum + tc }
-
48
@assertion_count = results.inject(0) { |sum, (_, ac)| sum + ac }
-
-
1
output.sync = old_sync if sync
-
-
1
t = Time.now - start
-
-
1
puts
-
1
puts
-
puts "Finished #{type}s in %.6fs, %.4f tests/s, %.4f assertions/s." %
-
1
[t, test_count / t, assertion_count / t]
-
-
1
report.each_with_index do |msg, i|
-
puts "\n%3d) %s" % [i + 1, msg]
-
end
-
-
1
puts
-
-
1
status
-
end
-
-
##
-
# Runs all the +suites+ for a given +type+. Runs suites declaring
-
# a test_order of +:parallel+ in parallel, and everything else
-
# serial.
-
-
1
def _run_suites suites, type
-
48
parallel, serial = suites.partition { |s| s.test_order == :parallel }
-
-
ParallelEach.new(parallel).map { |suite| _run_suite suite, type } +
-
48
serial.map { |suite| _run_suite suite, type }
-
end
-
-
##
-
# Run a single +suite+ for a given +type+.
-
-
1
def _run_suite suite, type
-
47
header = "#{type}_suite_header"
-
47
puts send(header, suite) if respond_to? header
-
-
47
filter = options[:filter] || '/./'
-
47
filter = Regexp.new $1 if filter =~ /\/(.*)\//
-
-
47
assertions = suite.send("#{type}_methods").grep(filter).map { |method|
-
146
inst = suite.new method
-
146
inst._assertions = 0
-
-
146
print "#{suite}##{method} = " if @verbose
-
-
146
start_time = Time.now if @verbose
-
146
result = inst.run self
-
-
146
print "%.2f s = " % (Time.now - start_time) if @verbose
-
146
print result
-
146
puts if @verbose
-
-
146
inst._assertions
-
}
-
-
193
return assertions.size, assertions.inject(0) { |sum, n| sum + n }
-
end
-
-
##
-
# Record the result of a single run. Makes it very easy to gather
-
# information. Eg:
-
#
-
# class StatisticsRecorder < MiniTest::Unit
-
# def record suite, method, assertions, time, error
-
# # ... record the results somewhere ...
-
# end
-
# end
-
#
-
# MiniTest::Unit.runner = StatisticsRecorder.new
-
-
1
def record suite, method, assertions, time, error
-
end
-
-
1
def location e # :nodoc:
-
last_before_assertion = ""
-
e.backtrace.reverse_each do |s|
-
break if s =~ /in .(assert|refute|flunk|pass|fail|raise|must|wont)/
-
last_before_assertion = s
-
end
-
last_before_assertion.sub(/:in .*$/, '')
-
end
-
-
##
-
# Writes status for failed test +meth+ in +klass+ which finished with
-
# exception +e+
-
-
1
def puke klass, meth, e
-
e = case e
-
when MiniTest::Skip then
-
@skips += 1
-
return "S" unless @verbose
-
"Skipped:\n#{meth}(#{klass}) [#{location e}]:\n#{e.message}\n"
-
when MiniTest::Assertion then
-
@failures += 1
-
"Failure:\n#{meth}(#{klass}) [#{location e}]:\n#{e.message}\n"
-
else
-
@errors += 1
-
bt = MiniTest::filter_backtrace(e.backtrace).join "\n "
-
"Error:\n#{meth}(#{klass}):\n#{e.class}: #{e.message}\n #{bt}\n"
-
end
-
@report << e
-
e[0, 1]
-
end
-
-
1
def initialize # :nodoc:
-
2
@report = []
-
2
@errors = @failures = @skips = 0
-
2
@verbose = false
-
2
@mutex = Mutex.new
-
end
-
-
1
def synchronize # :nodoc:
-
@mutex.synchronize { yield }
-
end
-
-
1
def process_args args = [] # :nodoc:
-
1
options = {}
-
1
orig_args = args.dup
-
-
1
OptionParser.new do |opts|
-
1
opts.banner = 'minitest options:'
-
1
opts.version = MiniTest::Unit::VERSION
-
-
1
opts.on '-h', '--help', 'Display this help.' do
-
puts opts
-
exit
-
end
-
-
1
opts.on '-s', '--seed SEED', Integer, "Sets random seed" do |m|
-
options[:seed] = m.to_i
-
end
-
-
1
opts.on '-v', '--verbose', "Verbose. Show progress processing files." do
-
options[:verbose] = true
-
end
-
-
1
opts.on '-n', '--name PATTERN', "Filter test names on pattern (e.g. /foo/)" do |a|
-
options[:filter] = a
-
end
-
-
1
opts.parse! args
-
1
orig_args -= args
-
end
-
-
1
unless options[:seed] then
-
1
srand
-
1
options[:seed] = srand % 0xFFFF
-
1
orig_args << "--seed" << options[:seed].to_s
-
end
-
-
1
srand options[:seed]
-
-
1
self.verbose = options[:verbose]
-
3
@help = orig_args.map { |s| s =~ /[\s|&<>$()]/ ? s.inspect : s }.join " "
-
-
1
options
-
end
-
-
##
-
# Begins the full test run. Delegates to +runner+'s #_run method.
-
-
1
def run args = []
-
1
self.class.runner._run(args)
-
end
-
-
##
-
# Top level driver, controls all output and filtering.
-
-
1
def _run args = []
-
1
self.options = process_args args
-
-
1
puts "Run options: #{help}"
-
-
1
self.class.plugins.each do |plugin|
-
1
send plugin
-
1
break unless report.empty?
-
end
-
-
1
return failures + errors if @test_count > 0 # or return nil...
-
rescue Interrupt
-
abort 'Interrupted'
-
end
-
-
##
-
# Runs test suites matching +filter+.
-
-
1
def run_tests
-
1
_run_anything :test
-
end
-
-
##
-
# Writes status to +io+
-
-
1
def status io = self.output
-
1
format = "%d tests, %d assertions, %d failures, %d errors, %d skips"
-
1
io.puts format % [test_count, assertion_count, failures, errors, skips]
-
end
-
-
##
-
# Provides a simple set of guards that you can use in your tests
-
# to skip execution if it is not applicable. These methods are
-
# mixed into TestCase as both instance and class methods so you
-
# can use them inside or outside of the test methods.
-
#
-
# def test_something_for_mri
-
# skip "bug 1234" if jruby?
-
# # ...
-
# end
-
#
-
# if windows? then
-
# # ... lots of test methods ...
-
# end
-
-
1
module Guard
-
-
##
-
# Is this running on jruby?
-
-
1
def jruby? platform = RUBY_PLATFORM
-
"java" == platform
-
end
-
-
##
-
# Is this running on mri?
-
-
1
def mri? platform = RUBY_DESCRIPTION
-
/^ruby/ =~ platform
-
end
-
-
##
-
# Is this running on rubinius?
-
-
1
def rubinius? platform = defined?(RUBY_ENGINE) && RUBY_ENGINE
-
"rbx" == platform
-
end
-
-
##
-
# Is this running on windows?
-
-
1
def windows? platform = RUBY_PLATFORM
-
/mswin|mingw/ =~ platform
-
end
-
end
-
-
##
-
# Provides before/after hooks for setup and teardown. These are
-
# meant for library writers, NOT for regular test authors. See
-
# #before_setup for an example.
-
-
1
module LifecycleHooks
-
##
-
# Runs before every test, after setup. This hook is meant for
-
# libraries to extend minitest. It is not meant to be used by
-
# test developers.
-
#
-
# See #before_setup for an example.
-
-
1
def after_setup; end
-
-
##
-
# Runs before every test, before setup. This hook is meant for
-
# libraries to extend minitest. It is not meant to be used by
-
# test developers.
-
#
-
# As a simplistic example:
-
#
-
# module MyMinitestPlugin
-
# def before_setup
-
# super
-
# # ... stuff to do before setup is run
-
# end
-
#
-
# def after_setup
-
# # ... stuff to do after setup is run
-
# super
-
# end
-
#
-
# def before_teardown
-
# super
-
# # ... stuff to do before teardown is run
-
# end
-
#
-
# def after_teardown
-
# # ... stuff to do after teardown is run
-
# super
-
# end
-
# end
-
#
-
# class MiniTest::Unit::TestCase
-
# include MyMinitestPlugin
-
# end
-
-
1
def before_setup; end
-
-
##
-
# Runs after every test, before teardown. This hook is meant for
-
# libraries to extend minitest. It is not meant to be used by
-
# test developers.
-
#
-
# See #before_setup for an example.
-
-
1
def before_teardown; end
-
-
##
-
# Runs after every test, after teardown. This hook is meant for
-
# libraries to extend minitest. It is not meant to be used by
-
# test developers.
-
#
-
# See #before_setup for an example.
-
-
1
def after_teardown; end
-
end
-
-
1
module Deprecated # :nodoc:
-
-
##
-
# This entire module is deprecated and slated for removal on 2013-01-01.
-
-
1
module Hooks
-
1
def run_setup_hooks # :nodoc:
-
_run_hooks self.class.setup_hooks
-
end
-
-
1
def _run_hooks hooks # :nodoc:
-
hooks.each do |hook|
-
if hook.respond_to?(:arity) && hook.arity == 1
-
hook.call(self)
-
else
-
hook.call
-
end
-
end
-
end
-
-
1
def run_teardown_hooks # :nodoc:
-
_run_hooks self.class.teardown_hooks.reverse
-
end
-
end
-
-
##
-
# This entire module is deprecated and slated for removal on 2013-01-01.
-
-
1
module HooksCM
-
##
-
# Adds a block of code that will be executed before every
-
# TestCase is run.
-
#
-
# NOTE: This method is deprecated, use before/after_setup. It
-
# will be removed on 2013-01-01.
-
-
1
def add_setup_hook arg=nil, &block
-
warn "NOTE: MiniTest::Unit::TestCase.add_setup_hook is deprecated, use before/after_setup via a module (and call super!). It will be removed on 2013-01-01. Called from #{caller.first}"
-
hook = arg || block
-
@setup_hooks << hook
-
end
-
-
1
def setup_hooks # :nodoc:
-
if superclass.respond_to? :setup_hooks then
-
superclass.setup_hooks
-
else
-
[]
-
end + @setup_hooks
-
end
-
-
##
-
# Adds a block of code that will be executed after every
-
# TestCase is run.
-
#
-
# NOTE: This method is deprecated, use before/after_teardown. It
-
# will be removed on 2013-01-01.
-
-
1
def add_teardown_hook arg=nil, &block
-
warn "NOTE: MiniTest::Unit::TestCase#add_teardown_hook is deprecated, use before/after_teardown. It will be removed on 2013-01-01. Called from #{caller.first}"
-
hook = arg || block
-
@teardown_hooks << hook
-
end
-
-
1
def teardown_hooks # :nodoc:
-
if superclass.respond_to? :teardown_hooks then
-
superclass.teardown_hooks
-
else
-
[]
-
end + @teardown_hooks
-
end
-
end
-
end
-
-
##
-
# Subclass TestCase to create your own tests. Typically you'll want a
-
# TestCase subclass per implementation class.
-
#
-
# See MiniTest::Assertions
-
-
1
class TestCase
-
1
include LifecycleHooks
-
1
include Deprecated::Hooks
-
1
extend Deprecated::HooksCM # UGH... I can't wait 'til 2013!
-
1
include Guard
-
1
extend Guard
-
-
1
attr_reader :__name__ # :nodoc:
-
-
1
PASSTHROUGH_EXCEPTIONS = [NoMemoryError, SignalException,
-
Interrupt, SystemExit] # :nodoc:
-
-
1
SUPPORTS_INFO_SIGNAL = Signal.list['INFO'] # :nodoc:
-
-
##
-
# Runs the tests reporting the status to +runner+
-
-
1
def run runner
-
trap "INFO" do
-
runner.report.each_with_index do |msg, i|
-
warn "\n%3d) %s" % [i + 1, msg]
-
end
-
warn ''
-
time = runner.start_time ? Time.now - runner.start_time : 0
-
warn "Current Test: %s#%s %.2fs" % [self.class, self.__name__, time]
-
runner.status $stderr
-
146
end if SUPPORTS_INFO_SIGNAL
-
-
146
start_time = Time.now
-
-
146
result = ""
-
146
begin
-
146
@passed = nil
-
146
self.before_setup
-
146
self.setup
-
146
self.after_setup
-
146
self.run_test self.__name__
-
146
result = "." unless io?
-
146
time = Time.now - start_time
-
146
runner.record self.class, self.__name__, self._assertions, time, nil
-
146
@passed = true
-
rescue *PASSTHROUGH_EXCEPTIONS
-
raise
-
rescue Exception => e
-
@passed = false
-
time = Time.now - start_time
-
runner.record self.class, self.__name__, self._assertions, time, e
-
result = runner.puke self.class, self.__name__, e
-
ensure
-
146
%w{ before_teardown teardown after_teardown }.each do |hook|
-
438
begin
-
438
self.send hook
-
rescue *PASSTHROUGH_EXCEPTIONS
-
raise
-
rescue Exception => e
-
@passed = false
-
result = runner.puke self.class, self.__name__, e
-
end
-
end
-
146
trap 'INFO', 'DEFAULT' if SUPPORTS_INFO_SIGNAL
-
end
-
146
result
-
end
-
-
1
alias :run_test :__send__
-
-
1
def initialize name # :nodoc:
-
146
@__name__ = name
-
146
@__io__ = nil
-
146
@passed = nil
-
146
@@current = self
-
end
-
-
1
def self.current # :nodoc:
-
@@current
-
end
-
-
##
-
# Return the output IO object
-
-
1
def io
-
@__io__ = true
-
MiniTest::Unit.output
-
end
-
-
##
-
# Have we hooked up the IO yet?
-
-
1
def io?
-
146
@__io__
-
end
-
-
1
def self.reset # :nodoc:
-
1
@@test_suites = {}
-
end
-
-
1
reset
-
-
##
-
# Call this at the top of your tests when you absolutely
-
# positively need to have ordered tests. In doing so, you're
-
# admitting that you suck and your tests are weak.
-
-
1
def self.i_suck_and_my_tests_are_order_dependent!
-
class << self
-
undef_method :test_order if method_defined? :test_order
-
define_method :test_order do :alpha end
-
end
-
end
-
-
##
-
# Make diffs for this TestCase use #pretty_inspect so that diff
-
# in assert_equal can be more details. NOTE: this is much slower
-
# than the regular inspect but much more usable for complex
-
# objects.
-
-
1
def self.make_my_diffs_pretty!
-
require 'pp'
-
-
define_method :mu_pp do |o|
-
o.pretty_inspect
-
end
-
end
-
-
##
-
# Call this at the top of your tests when you want to run your
-
# tests in parallel. In doing so, you're admitting that you rule
-
# and your tests are awesome.
-
-
1
def self.parallelize_me!
-
class << self
-
undef_method :test_order if method_defined? :test_order
-
define_method :test_order do :parallel end
-
end
-
end
-
-
1
def self.inherited klass # :nodoc:
-
47
@@test_suites[klass] = true
-
47
klass.reset_setup_teardown_hooks
-
47
super
-
end
-
-
1
def self.test_order # :nodoc:
-
2
:random
-
end
-
-
1
def self.test_suites # :nodoc:
-
48
@@test_suites.keys.sort_by { |ts| ts.name.to_s }
-
end
-
-
1
def self.test_methods # :nodoc:
-
193
methods = public_instance_methods(true).grep(/^test/).map { |m| m.to_s }
-
-
47
case self.test_order
-
when :parallel
-
max = methods.size
-
ParallelEach.new methods.sort.sort_by { rand max }
-
when :random then
-
1
max = methods.size
-
1
methods.sort.sort_by { rand max }
-
when :alpha, :sorted then
-
46
methods.sort
-
else
-
raise "Unknown test_order: #{self.test_order.inspect}"
-
end
-
end
-
-
##
-
# Returns true if the test passed.
-
-
1
def passed?
-
@passed
-
end
-
-
##
-
# Runs before every test. Use this to set up before each test
-
# run.
-
-
1
def setup; end
-
-
##
-
# Runs after every test. Use this to clean up after each test
-
# run.
-
-
1
def teardown; end
-
-
1
def self.reset_setup_teardown_hooks # :nodoc:
-
# also deprecated... believe it.
-
48
@setup_hooks = []
-
48
@teardown_hooks = []
-
end
-
-
1
reset_setup_teardown_hooks
-
-
1
include MiniTest::Assertions
-
end # class TestCase
-
end # class Unit
-
end # module MiniTest
-
-
1
Minitest = MiniTest # :nodoc: because ugh... I typo this all the time
-
-
1
if $DEBUG then
-
module Test # :nodoc:
-
module Unit # :nodoc:
-
class TestCase # :nodoc:
-
def self.inherited x # :nodoc:
-
# this helps me ferret out porting issues
-
raise "Using minitest and test/unit in the same process: #{x}"
-
end
-
end
-
end
-
end
-
end
-
# Copyright (C) 2007, 2008, 2009, 2010 Christian Neukirchen <purl.org/net/chneukirchen>
-
#
-
# Rack is freely distributable under the terms of an MIT-style license.
-
# See COPYING or http://www.opensource.org/licenses/mit-license.php.
-
-
# The Rack main module, serving as a namespace for all core Rack
-
# modules and classes.
-
#
-
# All modules meant for use in your application are <tt>autoload</tt>ed here,
-
# so it should be enough just to <tt>require rack.rb</tt> in your code.
-
-
1
module Rack
-
# The Rack protocol version number implemented.
-
1
VERSION = [1,1]
-
-
# Return the Rack protocol version as a dotted string.
-
1
def self.version
-
VERSION.join(".")
-
end
-
-
# Return the Rack release as a dotted string.
-
1
def self.release
-
"1.4"
-
end
-
-
1
autoload :Builder, "rack/builder"
-
1
autoload :BodyProxy, "rack/body_proxy"
-
1
autoload :Cascade, "rack/cascade"
-
1
autoload :Chunked, "rack/chunked"
-
1
autoload :CommonLogger, "rack/commonlogger"
-
1
autoload :ConditionalGet, "rack/conditionalget"
-
1
autoload :Config, "rack/config"
-
1
autoload :ContentLength, "rack/content_length"
-
1
autoload :ContentType, "rack/content_type"
-
1
autoload :ETag, "rack/etag"
-
1
autoload :File, "rack/file"
-
1
autoload :Deflater, "rack/deflater"
-
1
autoload :Directory, "rack/directory"
-
1
autoload :ForwardRequest, "rack/recursive"
-
1
autoload :Handler, "rack/handler"
-
1
autoload :Head, "rack/head"
-
1
autoload :Lint, "rack/lint"
-
1
autoload :Lock, "rack/lock"
-
1
autoload :Logger, "rack/logger"
-
1
autoload :MethodOverride, "rack/methodoverride"
-
1
autoload :Mime, "rack/mime"
-
1
autoload :NullLogger, "rack/nulllogger"
-
1
autoload :Recursive, "rack/recursive"
-
1
autoload :Reloader, "rack/reloader"
-
1
autoload :Runtime, "rack/runtime"
-
1
autoload :Sendfile, "rack/sendfile"
-
1
autoload :Server, "rack/server"
-
1
autoload :ShowExceptions, "rack/showexceptions"
-
1
autoload :ShowStatus, "rack/showstatus"
-
1
autoload :Static, "rack/static"
-
1
autoload :URLMap, "rack/urlmap"
-
1
autoload :Utils, "rack/utils"
-
1
autoload :Multipart, "rack/multipart"
-
-
1
autoload :MockRequest, "rack/mock"
-
1
autoload :MockResponse, "rack/mock"
-
-
1
autoload :Request, "rack/request"
-
1
autoload :Response, "rack/response"
-
-
1
module Auth
-
1
autoload :Basic, "rack/auth/basic"
-
1
autoload :AbstractRequest, "rack/auth/abstract/request"
-
1
autoload :AbstractHandler, "rack/auth/abstract/handler"
-
1
module Digest
-
1
autoload :MD5, "rack/auth/digest/md5"
-
1
autoload :Nonce, "rack/auth/digest/nonce"
-
1
autoload :Params, "rack/auth/digest/params"
-
1
autoload :Request, "rack/auth/digest/request"
-
end
-
end
-
-
1
module Session
-
1
autoload :Cookie, "rack/session/cookie"
-
1
autoload :Pool, "rack/session/pool"
-
1
autoload :Memcache, "rack/session/memcache"
-
end
-
end
-
1
require 'rack/utils'
-
-
1
module Rack
-
-
# Middleware that applies chunked transfer encoding to response bodies
-
# when the response does not include a Content-Length header.
-
1
class Chunked
-
1
include Rack::Utils
-
-
# A body wrapper that emits chunked responses
-
1
class Body
-
1
TERM = "\r\n"
-
1
TAIL = "0#{TERM}#{TERM}"
-
-
1
include Rack::Utils
-
-
1
def initialize(body)
-
@body = body
-
end
-
-
1
def each
-
term = TERM
-
@body.each do |chunk|
-
size = bytesize(chunk)
-
next if size == 0
-
-
chunk = chunk.dup.force_encoding(Encoding::BINARY) if chunk.respond_to?(:force_encoding)
-
yield [size.to_s(16), term, chunk, term].join
-
end
-
yield TAIL
-
end
-
-
1
def close
-
@body.close if @body.respond_to?(:close)
-
end
-
end
-
-
1
def initialize(app)
-
@app = app
-
end
-
-
1
def call(env)
-
status, headers, body = @app.call(env)
-
headers = HeaderHash.new(headers)
-
-
if env['HTTP_VERSION'] == 'HTTP/1.0' ||
-
STATUS_WITH_NO_ENTITY_BODY.include?(status) ||
-
headers['Content-Length'] ||
-
headers['Transfer-Encoding']
-
[status, headers, body]
-
else
-
headers.delete('Content-Length')
-
headers['Transfer-Encoding'] = 'chunked'
-
[status, headers, Body.new(body)]
-
end
-
end
-
end
-
end
-
1
require 'rack/utils'
-
-
1
module Rack
-
# Rack::Lint validates your application and the requests and
-
# responses according to the Rack spec.
-
-
1
class Lint
-
1
def initialize(app)
-
@app = app
-
@content_length = nil
-
end
-
-
# :stopdoc:
-
-
1
class LintError < RuntimeError; end
-
1
module Assertion
-
1
def assert(message, &block)
-
unless block.call
-
raise LintError, message
-
end
-
end
-
end
-
1
include Assertion
-
-
## This specification aims to formalize the Rack protocol. You
-
## can (and should) use Rack::Lint to enforce it.
-
##
-
## When you develop middleware, be sure to add a Lint before and
-
## after to catch all mistakes.
-
-
## = Rack applications
-
-
## A Rack application is a Ruby object (not a class) that
-
## responds to +call+.
-
1
def call(env=nil)
-
dup._call(env)
-
end
-
-
1
def _call(env)
-
## It takes exactly one argument, the *environment*
-
assert("No env given") { env }
-
check_env env
-
-
env['rack.input'] = InputWrapper.new(env['rack.input'])
-
env['rack.errors'] = ErrorWrapper.new(env['rack.errors'])
-
-
## and returns an Array of exactly three values:
-
status, headers, @body = @app.call(env)
-
## The *status*,
-
check_status status
-
## the *headers*,
-
check_headers headers
-
## and the *body*.
-
check_content_type status, headers
-
check_content_length status, headers
-
@head_request = env["REQUEST_METHOD"] == "HEAD"
-
[status, headers, self]
-
end
-
-
## == The Environment
-
1
def check_env(env)
-
## The environment must be an instance of Hash that includes
-
## CGI-like headers. The application is free to modify the
-
## environment.
-
assert("env #{env.inspect} is not a Hash, but #{env.class}") {
-
env.kind_of? Hash
-
}
-
-
##
-
## The environment is required to include these variables
-
## (adopted from PEP333), except when they'd be empty, but see
-
## below.
-
-
## <tt>REQUEST_METHOD</tt>:: The HTTP request method, such as
-
## "GET" or "POST". This cannot ever
-
## be an empty string, and so is
-
## always required.
-
-
## <tt>SCRIPT_NAME</tt>:: The initial portion of the request
-
## URL's "path" that corresponds to the
-
## application object, so that the
-
## application knows its virtual
-
## "location". This may be an empty
-
## string, if the application corresponds
-
## to the "root" of the server.
-
-
## <tt>PATH_INFO</tt>:: The remainder of the request URL's
-
## "path", designating the virtual
-
## "location" of the request's target
-
## within the application. This may be an
-
## empty string, if the request URL targets
-
## the application root and does not have a
-
## trailing slash. This value may be
-
## percent-encoded when I originating from
-
## a URL.
-
-
## <tt>QUERY_STRING</tt>:: The portion of the request URL that
-
## follows the <tt>?</tt>, if any. May be
-
## empty, but is always required!
-
-
## <tt>SERVER_NAME</tt>, <tt>SERVER_PORT</tt>:: When combined with <tt>SCRIPT_NAME</tt> and <tt>PATH_INFO</tt>, these variables can be used to complete the URL. Note, however, that <tt>HTTP_HOST</tt>, if present, should be used in preference to <tt>SERVER_NAME</tt> for reconstructing the request URL. <tt>SERVER_NAME</tt> and <tt>SERVER_PORT</tt> can never be empty strings, and so are always required.
-
-
## <tt>HTTP_</tt> Variables:: Variables corresponding to the
-
## client-supplied HTTP request
-
## headers (i.e., variables whose
-
## names begin with <tt>HTTP_</tt>). The
-
## presence or absence of these
-
## variables should correspond with
-
## the presence or absence of the
-
## appropriate HTTP header in the
-
## request.
-
-
## In addition to this, the Rack environment must include these
-
## Rack-specific variables:
-
-
## <tt>rack.version</tt>:: The Array [1,1], representing this version of Rack.
-
## <tt>rack.url_scheme</tt>:: +http+ or +https+, depending on the request URL.
-
## <tt>rack.input</tt>:: See below, the input stream.
-
## <tt>rack.errors</tt>:: See below, the error stream.
-
## <tt>rack.multithread</tt>:: true if the application object may be simultaneously invoked by another thread in the same process, false otherwise.
-
## <tt>rack.multiprocess</tt>:: true if an equivalent application object may be simultaneously invoked by another process, false otherwise.
-
## <tt>rack.run_once</tt>:: true if the server expects (but does not guarantee!) that the application will only be invoked this one time during the life of its containing process. Normally, this will only be true for a server based on CGI (or something similar).
-
##
-
-
## Additional environment specifications have approved to
-
## standardized middleware APIs. None of these are required to
-
## be implemented by the server.
-
-
## <tt>rack.session</tt>:: A hash like interface for storing request session data.
-
## The store must implement:
-
if session = env['rack.session']
-
## store(key, value) (aliased as []=);
-
assert("session #{session.inspect} must respond to store and []=") {
-
session.respond_to?(:store) && session.respond_to?(:[]=)
-
}
-
-
## fetch(key, default = nil) (aliased as []);
-
assert("session #{session.inspect} must respond to fetch and []") {
-
session.respond_to?(:fetch) && session.respond_to?(:[])
-
}
-
-
## delete(key);
-
assert("session #{session.inspect} must respond to delete") {
-
session.respond_to?(:delete)
-
}
-
-
## clear;
-
assert("session #{session.inspect} must respond to clear") {
-
session.respond_to?(:clear)
-
}
-
end
-
-
## <tt>rack.logger</tt>:: A common object interface for logging messages.
-
## The object must implement:
-
if logger = env['rack.logger']
-
## info(message, &block)
-
assert("logger #{logger.inspect} must respond to info") {
-
logger.respond_to?(:info)
-
}
-
-
## debug(message, &block)
-
assert("logger #{logger.inspect} must respond to debug") {
-
logger.respond_to?(:debug)
-
}
-
-
## warn(message, &block)
-
assert("logger #{logger.inspect} must respond to warn") {
-
logger.respond_to?(:warn)
-
}
-
-
## error(message, &block)
-
assert("logger #{logger.inspect} must respond to error") {
-
logger.respond_to?(:error)
-
}
-
-
## fatal(message, &block)
-
assert("logger #{logger.inspect} must respond to fatal") {
-
logger.respond_to?(:fatal)
-
}
-
end
-
-
## The server or the application can store their own data in the
-
## environment, too. The keys must contain at least one dot,
-
## and should be prefixed uniquely. The prefix <tt>rack.</tt>
-
## is reserved for use with the Rack core distribution and other
-
## accepted specifications and must not be used otherwise.
-
##
-
-
%w[REQUEST_METHOD SERVER_NAME SERVER_PORT
-
QUERY_STRING
-
rack.version rack.input rack.errors
-
rack.multithread rack.multiprocess rack.run_once].each { |header|
-
assert("env missing required key #{header}") { env.include? header }
-
}
-
-
## The environment must not contain the keys
-
## <tt>HTTP_CONTENT_TYPE</tt> or <tt>HTTP_CONTENT_LENGTH</tt>
-
## (use the versions without <tt>HTTP_</tt>).
-
%w[HTTP_CONTENT_TYPE HTTP_CONTENT_LENGTH].each { |header|
-
assert("env contains #{header}, must use #{header[5,-1]}") {
-
not env.include? header
-
}
-
}
-
-
## The CGI keys (named without a period) must have String values.
-
env.each { |key, value|
-
next if key.include? "." # Skip extensions
-
assert("env variable #{key} has non-string value #{value.inspect}") {
-
value.kind_of? String
-
}
-
}
-
-
##
-
## There are the following restrictions:
-
-
## * <tt>rack.version</tt> must be an array of Integers.
-
assert("rack.version must be an Array, was #{env["rack.version"].class}") {
-
env["rack.version"].kind_of? Array
-
}
-
## * <tt>rack.url_scheme</tt> must either be +http+ or +https+.
-
assert("rack.url_scheme unknown: #{env["rack.url_scheme"].inspect}") {
-
%w[http https].include? env["rack.url_scheme"]
-
}
-
-
## * There must be a valid input stream in <tt>rack.input</tt>.
-
check_input env["rack.input"]
-
## * There must be a valid error stream in <tt>rack.errors</tt>.
-
check_error env["rack.errors"]
-
-
## * The <tt>REQUEST_METHOD</tt> must be a valid token.
-
assert("REQUEST_METHOD unknown: #{env["REQUEST_METHOD"]}") {
-
env["REQUEST_METHOD"] =~ /\A[0-9A-Za-z!\#$%&'*+.^_`|~-]+\z/
-
}
-
-
## * The <tt>SCRIPT_NAME</tt>, if non-empty, must start with <tt>/</tt>
-
assert("SCRIPT_NAME must start with /") {
-
!env.include?("SCRIPT_NAME") ||
-
env["SCRIPT_NAME"] == "" ||
-
env["SCRIPT_NAME"] =~ /\A\//
-
}
-
## * The <tt>PATH_INFO</tt>, if non-empty, must start with <tt>/</tt>
-
assert("PATH_INFO must start with /") {
-
!env.include?("PATH_INFO") ||
-
env["PATH_INFO"] == "" ||
-
env["PATH_INFO"] =~ /\A\//
-
}
-
## * The <tt>CONTENT_LENGTH</tt>, if given, must consist of digits only.
-
assert("Invalid CONTENT_LENGTH: #{env["CONTENT_LENGTH"]}") {
-
!env.include?("CONTENT_LENGTH") || env["CONTENT_LENGTH"] =~ /\A\d+\z/
-
}
-
-
## * One of <tt>SCRIPT_NAME</tt> or <tt>PATH_INFO</tt> must be
-
## set. <tt>PATH_INFO</tt> should be <tt>/</tt> if
-
## <tt>SCRIPT_NAME</tt> is empty.
-
assert("One of SCRIPT_NAME or PATH_INFO must be set (make PATH_INFO '/' if SCRIPT_NAME is empty)") {
-
env["SCRIPT_NAME"] || env["PATH_INFO"]
-
}
-
## <tt>SCRIPT_NAME</tt> never should be <tt>/</tt>, but instead be empty.
-
assert("SCRIPT_NAME cannot be '/', make it '' and PATH_INFO '/'") {
-
env["SCRIPT_NAME"] != "/"
-
}
-
end
-
-
## === The Input Stream
-
##
-
## The input stream is an IO-like object which contains the raw HTTP
-
## POST data.
-
1
def check_input(input)
-
## When applicable, its external encoding must be "ASCII-8BIT" and it
-
## must be opened in binary mode, for Ruby 1.9 compatibility.
-
assert("rack.input #{input} does not have ASCII-8BIT as its external encoding") {
-
input.external_encoding.name == "ASCII-8BIT"
-
} if input.respond_to?(:external_encoding)
-
assert("rack.input #{input} is not opened in binary mode") {
-
input.binmode?
-
} if input.respond_to?(:binmode?)
-
-
## The input stream must respond to +gets+, +each+, +read+ and +rewind+.
-
[:gets, :each, :read, :rewind].each { |method|
-
assert("rack.input #{input} does not respond to ##{method}") {
-
input.respond_to? method
-
}
-
}
-
end
-
-
1
class InputWrapper
-
1
include Assertion
-
-
1
def initialize(input)
-
@input = input
-
end
-
-
## * +gets+ must be called without arguments and return a string,
-
## or +nil+ on EOF.
-
1
def gets(*args)
-
assert("rack.input#gets called with arguments") { args.size == 0 }
-
v = @input.gets
-
assert("rack.input#gets didn't return a String") {
-
v.nil? or v.kind_of? String
-
}
-
v
-
end
-
-
## * +read+ behaves like IO#read. Its signature is <tt>read([length, [buffer]])</tt>.
-
## If given, +length+ must be a non-negative Integer (>= 0) or +nil+, and +buffer+ must
-
## be a String and may not be nil. If +length+ is given and not nil, then this method
-
## reads at most +length+ bytes from the input stream. If +length+ is not given or nil,
-
## then this method reads all data until EOF.
-
## When EOF is reached, this method returns nil if +length+ is given and not nil, or ""
-
## if +length+ is not given or is nil.
-
## If +buffer+ is given, then the read data will be placed into +buffer+ instead of a
-
## newly created String object.
-
1
def read(*args)
-
assert("rack.input#read called with too many arguments") {
-
args.size <= 2
-
}
-
if args.size >= 1
-
assert("rack.input#read called with non-integer and non-nil length") {
-
args.first.kind_of?(Integer) || args.first.nil?
-
}
-
assert("rack.input#read called with a negative length") {
-
args.first.nil? || args.first >= 0
-
}
-
end
-
if args.size >= 2
-
assert("rack.input#read called with non-String buffer") {
-
args[1].kind_of?(String)
-
}
-
end
-
-
v = @input.read(*args)
-
-
assert("rack.input#read didn't return nil or a String") {
-
v.nil? or v.kind_of? String
-
}
-
if args[0].nil?
-
assert("rack.input#read(nil) returned nil on EOF") {
-
!v.nil?
-
}
-
end
-
-
v
-
end
-
-
## * +each+ must be called without arguments and only yield Strings.
-
1
def each(*args)
-
assert("rack.input#each called with arguments") { args.size == 0 }
-
@input.each { |line|
-
assert("rack.input#each didn't yield a String") {
-
line.kind_of? String
-
}
-
yield line
-
}
-
end
-
-
## * +rewind+ must be called without arguments. It rewinds the input
-
## stream back to the beginning. It must not raise Errno::ESPIPE:
-
## that is, it may not be a pipe or a socket. Therefore, handler
-
## developers must buffer the input data into some rewindable object
-
## if the underlying input stream is not rewindable.
-
1
def rewind(*args)
-
assert("rack.input#rewind called with arguments") { args.size == 0 }
-
assert("rack.input#rewind raised Errno::ESPIPE") {
-
begin
-
@input.rewind
-
true
-
rescue Errno::ESPIPE
-
false
-
end
-
}
-
end
-
-
## * +close+ must never be called on the input stream.
-
1
def close(*args)
-
assert("rack.input#close must not be called") { false }
-
end
-
end
-
-
## === The Error Stream
-
1
def check_error(error)
-
## The error stream must respond to +puts+, +write+ and +flush+.
-
[:puts, :write, :flush].each { |method|
-
assert("rack.error #{error} does not respond to ##{method}") {
-
error.respond_to? method
-
}
-
}
-
end
-
-
1
class ErrorWrapper
-
1
include Assertion
-
-
1
def initialize(error)
-
@error = error
-
end
-
-
## * +puts+ must be called with a single argument that responds to +to_s+.
-
1
def puts(str)
-
@error.puts str
-
end
-
-
## * +write+ must be called with a single argument that is a String.
-
1
def write(str)
-
assert("rack.errors#write not called with a String") { str.kind_of? String }
-
@error.write str
-
end
-
-
## * +flush+ must be called without arguments and must be called
-
## in order to make the error appear for sure.
-
1
def flush
-
@error.flush
-
end
-
-
## * +close+ must never be called on the error stream.
-
1
def close(*args)
-
assert("rack.errors#close must not be called") { false }
-
end
-
end
-
-
## == The Response
-
-
## === The Status
-
1
def check_status(status)
-
## This is an HTTP status. When parsed as integer (+to_i+), it must be
-
## greater than or equal to 100.
-
assert("Status must be >=100 seen as integer") { status.to_i >= 100 }
-
end
-
-
## === The Headers
-
1
def check_headers(header)
-
## The header must respond to +each+, and yield values of key and value.
-
assert("headers object should respond to #each, but doesn't (got #{header.class} as headers)") {
-
header.respond_to? :each
-
}
-
header.each { |key, value|
-
## The header keys must be Strings.
-
assert("header key must be a string, was #{key.class}") {
-
key.kind_of? String
-
}
-
## The header must not contain a +Status+ key,
-
assert("header must not contain Status") { key.downcase != "status" }
-
## contain keys with <tt>:</tt> or newlines in their name,
-
assert("header names must not contain : or \\n") { key !~ /[:\n]/ }
-
## contain keys names that end in <tt>-</tt> or <tt>_</tt>,
-
assert("header names must not end in - or _") { key !~ /[-_]\z/ }
-
## but only contain keys that consist of
-
## letters, digits, <tt>_</tt> or <tt>-</tt> and start with a letter.
-
assert("invalid header name: #{key}") { key =~ /\A[a-zA-Z][a-zA-Z0-9_-]*\z/ }
-
-
## The values of the header must be Strings,
-
assert("a header value must be a String, but the value of " +
-
"'#{key}' is a #{value.class}") { value.kind_of? String }
-
## consisting of lines (for multiple header values, e.g. multiple
-
## <tt>Set-Cookie</tt> values) seperated by "\n".
-
value.split("\n").each { |item|
-
## The lines must not contain characters below 037.
-
assert("invalid header value #{key}: #{item.inspect}") {
-
item !~ /[\000-\037]/
-
}
-
}
-
}
-
end
-
-
## === The Content-Type
-
1
def check_content_type(status, headers)
-
headers.each { |key, value|
-
## There must be a <tt>Content-Type</tt>, except when the
-
## +Status+ is 1xx, 204, 205 or 304, in which case there must be none
-
## given.
-
if key.downcase == "content-type"
-
assert("Content-Type header found in #{status} response, not allowed") {
-
not Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include? status.to_i
-
}
-
return
-
end
-
}
-
assert("No Content-Type header found") {
-
Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include? status.to_i
-
}
-
end
-
-
## === The Content-Length
-
1
def check_content_length(status, headers)
-
headers.each { |key, value|
-
if key.downcase == 'content-length'
-
## There must not be a <tt>Content-Length</tt> header when the
-
## +Status+ is 1xx, 204, 205 or 304.
-
assert("Content-Length header found in #{status} response, not allowed") {
-
not Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include? status.to_i
-
}
-
@content_length = value
-
end
-
}
-
end
-
-
1
def verify_content_length(bytes)
-
if @head_request
-
assert("Response body was given for HEAD request, but should be empty") {
-
bytes == 0
-
}
-
elsif @content_length
-
assert("Content-Length header was #{@content_length}, but should be #{bytes}") {
-
@content_length == bytes.to_s
-
}
-
end
-
end
-
-
## === The Body
-
1
def each
-
@closed = false
-
bytes = 0
-
-
## The Body must respond to +each+
-
assert("Response body must respond to each") do
-
@body.respond_to?(:each)
-
end
-
-
@body.each { |part|
-
## and must only yield String values.
-
assert("Body yielded non-string value #{part.inspect}") {
-
part.kind_of? String
-
}
-
bytes += Rack::Utils.bytesize(part)
-
yield part
-
}
-
verify_content_length(bytes)
-
-
##
-
## The Body itself should not be an instance of String, as this will
-
## break in Ruby 1.9.
-
##
-
## If the Body responds to +close+, it will be called after iteration.
-
# XXX howto: assert("Body has not been closed") { @closed }
-
-
-
##
-
## If the Body responds to +to_path+, it must return a String
-
## identifying the location of a file whose contents are identical
-
## to that produced by calling +each+; this may be used by the
-
## server as an alternative, possibly more efficient way to
-
## transport the response.
-
-
if @body.respond_to?(:to_path)
-
assert("The file identified by body.to_path does not exist") {
-
::File.exist? @body.to_path
-
}
-
end
-
-
##
-
## The Body commonly is an Array of Strings, the application
-
## instance itself, or a File-like object.
-
end
-
-
1
def close
-
@closed = true
-
@body.close if @body.respond_to?(:close)
-
end
-
-
# :startdoc:
-
-
end
-
end
-
-
## == Thanks
-
## Some parts of this specification are adopted from PEP333: Python
-
## Web Server Gateway Interface
-
## v1.0 (http://www.python.org/dev/peps/pep-0333/). I'd like to thank
-
## everyone involved in that effort.
-
1
require 'uri'
-
1
require 'stringio'
-
1
require 'rack'
-
1
require 'rack/lint'
-
1
require 'rack/utils'
-
1
require 'rack/response'
-
-
1
module Rack
-
# Rack::MockRequest helps testing your Rack application without
-
# actually using HTTP.
-
#
-
# After performing a request on a URL with get/post/put/delete, it
-
# returns a MockResponse with useful helper methods for effective
-
# testing.
-
#
-
# You can pass a hash with additional configuration to the
-
# get/post/put/delete.
-
# <tt>:input</tt>:: A String or IO-like to be used as rack.input.
-
# <tt>:fatal</tt>:: Raise a FatalWarning if the app writes to rack.errors.
-
# <tt>:lint</tt>:: If true, wrap the application in a Rack::Lint.
-
-
1
class MockRequest
-
1
class FatalWarning < RuntimeError
-
end
-
-
1
class FatalWarner
-
1
def puts(warning)
-
raise FatalWarning, warning
-
end
-
-
1
def write(warning)
-
raise FatalWarning, warning
-
end
-
-
1
def flush
-
end
-
-
1
def string
-
""
-
end
-
end
-
-
1
DEFAULT_ENV = {
-
"rack.version" => Rack::VERSION,
-
"rack.input" => StringIO.new,
-
"rack.errors" => StringIO.new,
-
"rack.multithread" => true,
-
"rack.multiprocess" => true,
-
"rack.run_once" => false,
-
}
-
-
1
def initialize(app)
-
@app = app
-
end
-
-
1
def get(uri, opts={}) request("GET", uri, opts) end
-
1
def post(uri, opts={}) request("POST", uri, opts) end
-
1
def put(uri, opts={}) request("PUT", uri, opts) end
-
1
def delete(uri, opts={}) request("DELETE", uri, opts) end
-
1
def head(uri, opts={}) request("HEAD", uri, opts) end
-
-
1
def request(method="GET", uri="", opts={})
-
env = self.class.env_for(uri, opts.merge(:method => method))
-
-
if opts[:lint]
-
app = Rack::Lint.new(@app)
-
else
-
app = @app
-
end
-
-
errors = env["rack.errors"]
-
status, headers, body = app.call(env)
-
MockResponse.new(status, headers, body, errors)
-
ensure
-
body.close if body.respond_to?(:close)
-
end
-
-
# Return the Rack environment used for a request to +uri+.
-
1
def self.env_for(uri="", opts={})
-
2
uri = URI(uri)
-
2
uri.path = "/#{uri.path}" unless uri.path[0] == ?/
-
-
2
env = DEFAULT_ENV.dup
-
-
2
env["REQUEST_METHOD"] = opts[:method] ? opts[:method].to_s.upcase : "GET"
-
2
env["SERVER_NAME"] = uri.host || "example.org"
-
2
env["SERVER_PORT"] = uri.port ? uri.port.to_s : "80"
-
2
env["QUERY_STRING"] = uri.query.to_s
-
2
env["PATH_INFO"] = (!uri.path || uri.path.empty?) ? "/" : uri.path
-
2
env["rack.url_scheme"] = uri.scheme || "http"
-
2
env["HTTPS"] = env["rack.url_scheme"] == "https" ? "on" : "off"
-
-
2
env["SCRIPT_NAME"] = opts[:script_name] || ""
-
-
2
if opts[:fatal]
-
env["rack.errors"] = FatalWarner.new
-
else
-
2
env["rack.errors"] = StringIO.new
-
end
-
-
2
if params = opts[:params]
-
if env["REQUEST_METHOD"] == "GET"
-
params = Utils.parse_nested_query(params) if params.is_a?(String)
-
params.update(Utils.parse_nested_query(env["QUERY_STRING"]))
-
env["QUERY_STRING"] = Utils.build_nested_query(params)
-
elsif !opts.has_key?(:input)
-
opts["CONTENT_TYPE"] = "application/x-www-form-urlencoded"
-
if params.is_a?(Hash)
-
if data = Utils::Multipart.build_multipart(params)
-
opts[:input] = data
-
opts["CONTENT_LENGTH"] ||= data.length.to_s
-
opts["CONTENT_TYPE"] = "multipart/form-data; boundary=#{Utils::Multipart::MULTIPART_BOUNDARY}"
-
else
-
opts[:input] = Utils.build_nested_query(params)
-
end
-
else
-
opts[:input] = params
-
end
-
end
-
end
-
-
2
empty_str = ""
-
2
empty_str.force_encoding("ASCII-8BIT") if empty_str.respond_to? :force_encoding
-
2
opts[:input] ||= empty_str
-
2
if String === opts[:input]
-
2
rack_input = StringIO.new(opts[:input])
-
else
-
rack_input = opts[:input]
-
end
-
-
2
rack_input.set_encoding(Encoding::BINARY) if rack_input.respond_to?(:set_encoding)
-
2
env['rack.input'] = rack_input
-
-
2
env["CONTENT_LENGTH"] ||= env["rack.input"].length.to_s
-
-
2
opts.each { |field, value|
-
14
env[field] = value if String === field
-
}
-
-
2
env
-
end
-
end
-
-
# Rack::MockResponse provides useful helpers for testing your apps.
-
# Usually, you don't create the MockResponse on your own, but use
-
# MockRequest.
-
-
1
class MockResponse < Rack::Response
-
# Headers
-
1
attr_reader :original_headers
-
-
# Errors
-
1
attr_accessor :errors
-
-
1
def initialize(status, headers, body, errors=StringIO.new(""))
-
1
@original_headers = headers
-
1
@errors = errors.string if errors.respond_to?(:string)
-
1
@body_string = nil
-
-
1
super(body, status, headers)
-
end
-
-
1
def =~(other)
-
body =~ other
-
end
-
-
1
def match(other)
-
body.match other
-
end
-
-
1
def body
-
# FIXME: apparently users of MockResponse expect the return value of
-
# MockResponse#body to be a string. However, the real response object
-
# returns the body as a list.
-
#
-
# See spec_showstatus.rb:
-
#
-
# should "not replace existing messages" do
-
# ...
-
# res.body.should == "foo!"
-
# end
-
1
super.join
-
end
-
-
1
def empty?
-
[201, 204, 205, 304].include? status
-
end
-
end
-
end
-
1
module Rack
-
# A multipart form data parser, adapted from IOWA.
-
#
-
# Usually, Rack::Request#POST takes care of calling this.
-
1
module Multipart
-
1
autoload :UploadedFile, 'rack/multipart/uploaded_file'
-
1
autoload :Parser, 'rack/multipart/parser'
-
1
autoload :Generator, 'rack/multipart/generator'
-
-
1
EOL = "\r\n"
-
1
MULTIPART_BOUNDARY = "AaB03x"
-
1
MULTIPART = %r|\Amultipart/.*boundary=\"?([^\";,]+)\"?|n
-
1
TOKEN = /[^\s()<>,;:\\"\/\[\]?=]+/
-
1
CONDISP = /Content-Disposition:\s*#{TOKEN}\s*/i
-
1
DISPPARM = /;\s*(#{TOKEN})=("(?:\\"|[^"])*"|#{TOKEN})*/
-
1
RFC2183 = /^#{CONDISP}(#{DISPPARM})+$/i
-
1
BROKEN_QUOTED = /^#{CONDISP}.*;\sfilename="(.*?)"(?:\s*$|\s*;\s*#{TOKEN}=)/i
-
1
BROKEN_UNQUOTED = /^#{CONDISP}.*;\sfilename=(#{TOKEN})/i
-
1
MULTIPART_CONTENT_TYPE = /Content-Type: (.*)#{EOL}/ni
-
1
MULTIPART_CONTENT_DISPOSITION = /Content-Disposition:.*\s+name="?([^\";]*)"?/ni
-
1
MULTIPART_CONTENT_ID = /Content-ID:\s*([^#{EOL}]*)/ni
-
-
1
class << self
-
1
def parse_multipart(env)
-
1
Parser.new(env).parse
-
end
-
-
1
def build_multipart(params, first = true)
-
Generator.new(params, first).dump
-
end
-
end
-
-
end
-
end
-
1
require 'rack/utils'
-
-
1
module Rack
-
1
module Multipart
-
1
class Parser
-
1
BUFSIZE = 16384
-
-
1
def initialize(env)
-
1
@env = env
-
end
-
-
1
def parse
-
1
return nil unless setup_parse
-
-
fast_forward_to_first_boundary
-
-
loop do
-
head, filename, content_type, name, body =
-
get_current_head_and_filename_and_content_type_and_name_and_body
-
-
# Save the rest.
-
if i = @buf.index(rx)
-
body << @buf.slice!(0, i)
-
@buf.slice!(0, @boundary_size+2)
-
-
@content_length = -1 if $1 == "--"
-
end
-
-
filename, data = get_data(filename, body, content_type, name, head)
-
-
Utils.normalize_params(@params, name, data) unless data.nil?
-
-
# break if we're at the end of a buffer, but not if it is the end of a field
-
break if (@buf.empty? && $1 != EOL) || @content_length == -1
-
end
-
-
@io.rewind
-
-
@params.to_params_hash
-
end
-
-
1
private
-
1
def setup_parse
-
1
return false unless @env['CONTENT_TYPE'] =~ MULTIPART
-
-
@boundary = "--#{$1}"
-
-
@buf = ""
-
@params = Utils::KeySpaceConstrainedParams.new
-
-
@content_length = @env['CONTENT_LENGTH'].to_i
-
@io = @env['rack.input']
-
@io.rewind
-
-
@boundary_size = Utils.bytesize(@boundary) + EOL.size
-
-
@content_length -= @boundary_size
-
true
-
end
-
-
1
def full_boundary
-
@boundary + EOL
-
end
-
-
1
def rx
-
@rx ||= /(?:#{EOL})?#{Regexp.quote(@boundary)}(#{EOL}|--)/n
-
end
-
-
1
def fast_forward_to_first_boundary
-
loop do
-
read_buffer = @io.gets
-
break if read_buffer == full_boundary
-
raise EOFError, "bad content body" if read_buffer.nil?
-
end
-
end
-
-
1
def get_current_head_and_filename_and_content_type_and_name_and_body
-
head = nil
-
body = ''
-
filename = content_type = name = nil
-
content = nil
-
-
until head && @buf =~ rx
-
if !head && i = @buf.index(EOL+EOL)
-
head = @buf.slice!(0, i+2) # First \r\n
-
-
@buf.slice!(0, 2) # Second \r\n
-
-
content_type = head[MULTIPART_CONTENT_TYPE, 1]
-
name = head[MULTIPART_CONTENT_DISPOSITION, 1] || head[MULTIPART_CONTENT_ID, 1]
-
-
filename = get_filename(head)
-
-
if filename
-
body = Tempfile.new("RackMultipart")
-
body.binmode if body.respond_to?(:binmode)
-
end
-
-
next
-
end
-
-
# Save the read body part.
-
if head && (@boundary_size+4 < @buf.size)
-
body << @buf.slice!(0, @buf.size - (@boundary_size+4))
-
end
-
-
content = @io.read(BUFSIZE < @content_length ? BUFSIZE : @content_length)
-
raise EOFError, "bad content body" if content.nil? || content.empty?
-
-
@buf << content
-
@content_length -= content.size
-
end
-
-
[head, filename, content_type, name, body]
-
end
-
-
1
def get_filename(head)
-
filename = nil
-
if head =~ RFC2183
-
filename = Hash[head.scan(DISPPARM)]['filename']
-
filename = $1 if filename and filename =~ /^"(.*)"$/
-
elsif head =~ BROKEN_QUOTED
-
filename = $1
-
elsif head =~ BROKEN_UNQUOTED
-
filename = $1
-
end
-
-
if filename && filename.scan(/%.?.?/).all? { |s| s =~ /%[0-9a-fA-F]{2}/ }
-
filename = Utils.unescape(filename)
-
end
-
if filename && filename !~ /\\[^\\"]/
-
filename = filename.gsub(/\\(.)/, '\1')
-
end
-
filename
-
end
-
-
1
def get_data(filename, body, content_type, name, head)
-
data = nil
-
if filename == ""
-
# filename is blank which means no file has been selected
-
return data
-
elsif filename
-
body.rewind
-
-
# Take the basename of the upload's original filename.
-
# This handles the full Windows paths given by Internet Explorer
-
# (and perhaps other broken user agents) without affecting
-
# those which give the lone filename.
-
filename = filename.split(/[\/\\]/).last
-
-
data = {:filename => filename, :type => content_type,
-
:name => name, :tempfile => body, :head => head}
-
elsif !filename && content_type && body.is_a?(IO)
-
body.rewind
-
-
# Generic multipart cases, not coming from a form
-
data = {:type => content_type,
-
:name => name, :tempfile => body, :head => head}
-
else
-
data = body
-
end
-
-
[filename, data]
-
end
-
end
-
end
-
end
-
1
require 'rack/utils'
-
-
1
module Rack
-
# Rack::Request provides a convenient interface to a Rack
-
# environment. It is stateless, the environment +env+ passed to the
-
# constructor will be directly modified.
-
#
-
# req = Rack::Request.new(env)
-
# req.post?
-
# req.params["data"]
-
#
-
# The environment hash passed will store a reference to the Request object
-
# instantiated so that it will only instantiate if an instance of the Request
-
# object doesn't already exist.
-
-
1
class Request
-
# The environment of the request.
-
1
attr_reader :env
-
-
1
def initialize(env)
-
4
@env = env
-
end
-
-
1
def body; @env["rack.input"] end
-
2
def script_name; @env["SCRIPT_NAME"].to_s end
-
5
def path_info; @env["PATH_INFO"].to_s end
-
1
def request_method; @env["REQUEST_METHOD"] end
-
5
def query_string; @env["QUERY_STRING"].to_s end
-
1
def content_length; @env['CONTENT_LENGTH'] end
-
-
1
def content_type
-
content_type = @env['CONTENT_TYPE']
-
content_type.nil? || content_type.empty? ? nil : content_type
-
end
-
-
1
def session; @env['rack.session'] ||= {} end
-
1
def session_options; @env['rack.session.options'] ||= {} end
-
1
def logger; @env['rack.logger'] end
-
-
# The media type (type/subtype) portion of the CONTENT_TYPE header
-
# without any media type parameters. e.g., when CONTENT_TYPE is
-
# "text/plain;charset=utf-8", the media-type is "text/plain".
-
#
-
# For more information on the use of media types in HTTP, see:
-
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7
-
1
def media_type
-
content_type && content_type.split(/\s*[;,]\s*/, 2).first.downcase
-
end
-
-
# The media type parameters provided in CONTENT_TYPE as a Hash, or
-
# an empty Hash if no CONTENT_TYPE or media-type parameters were
-
# provided. e.g., when the CONTENT_TYPE is "text/plain;charset=utf-8",
-
# this method responds with the following Hash:
-
# { 'charset' => 'utf-8' }
-
1
def media_type_params
-
return {} if content_type.nil?
-
Hash[*content_type.split(/\s*[;,]\s*/)[1..-1].
-
collect { |s| s.split('=', 2) }.
-
map { |k,v| [k.downcase, v] }.flatten]
-
end
-
-
# The character set of the request body if a "charset" media type
-
# parameter was given, or nil if no "charset" was specified. Note
-
# that, per RFC2616, text/* media types that specify no explicit
-
# charset are to be considered ISO-8859-1.
-
1
def content_charset
-
media_type_params['charset']
-
end
-
-
1
def scheme
-
if @env['HTTPS'] == 'on'
-
'https'
-
elsif @env['HTTP_X_FORWARDED_SSL'] == 'on'
-
'https'
-
elsif @env['HTTP_X_FORWARDED_SCHEME']
-
@env['HTTP_X_FORWARDED_SCHEME']
-
elsif @env['HTTP_X_FORWARDED_PROTO']
-
@env['HTTP_X_FORWARDED_PROTO'].split(',')[0]
-
else
-
@env["rack.url_scheme"]
-
end
-
end
-
-
1
def ssl?
-
scheme == 'https'
-
end
-
-
1
def host_with_port
-
if forwarded = @env["HTTP_X_FORWARDED_HOST"]
-
forwarded.split(/,\s?/).last
-
else
-
@env['HTTP_HOST'] || "#{@env['SERVER_NAME'] || @env['SERVER_ADDR']}:#{@env['SERVER_PORT']}"
-
end
-
end
-
-
1
def port
-
if port = host_with_port.split(/:/)[1]
-
port.to_i
-
elsif port = @env['HTTP_X_FORWARDED_PORT']
-
port.to_i
-
elsif ssl?
-
443
-
elsif @env.has_key?("HTTP_X_FORWARDED_HOST")
-
80
-
else
-
@env["SERVER_PORT"].to_i
-
end
-
end
-
-
1
def host
-
# Remove port number.
-
host_with_port.to_s.gsub(/:\d+\z/, '')
-
end
-
-
1
def script_name=(s); @env["SCRIPT_NAME"] = s.to_s end
-
1
def path_info=(s); @env["PATH_INFO"] = s.to_s end
-
-
-
# Checks the HTTP request method (or verb) to see if it was of type DELETE
-
1
def delete?; request_method == "DELETE" end
-
-
# Checks the HTTP request method (or verb) to see if it was of type GET
-
1
def get?; request_method == "GET" end
-
-
# Checks the HTTP request method (or verb) to see if it was of type HEAD
-
1
def head?; request_method == "HEAD" end
-
-
# Checks the HTTP request method (or verb) to see if it was of type OPTIONS
-
1
def options?; request_method == "OPTIONS" end
-
-
# Checks the HTTP request method (or verb) to see if it was of type PATCH
-
1
def patch?; request_method == "PATCH" end
-
-
# Checks the HTTP request method (or verb) to see if it was of type POST
-
1
def post?; request_method == "POST" end
-
-
# Checks the HTTP request method (or verb) to see if it was of type PUT
-
1
def put?; request_method == "PUT" end
-
-
# Checks the HTTP request method (or verb) to see if it was of type TRACE
-
1
def trace?; request_method == "TRACE" end
-
-
-
# The set of form-data media-types. Requests that do not indicate
-
# one of the media types presents in this list will not be eligible
-
# for form-data / param parsing.
-
1
FORM_DATA_MEDIA_TYPES = [
-
'application/x-www-form-urlencoded',
-
'multipart/form-data'
-
]
-
-
# The set of media-types. Requests that do not indicate
-
# one of the media types presents in this list will not be eligible
-
# for param parsing like soap attachments or generic multiparts
-
1
PARSEABLE_DATA_MEDIA_TYPES = [
-
'multipart/related',
-
'multipart/mixed'
-
]
-
-
# Determine whether the request body contains form-data by checking
-
# the request Content-Type for one of the media-types:
-
# "application/x-www-form-urlencoded" or "multipart/form-data". The
-
# list of form-data media types can be modified through the
-
# +FORM_DATA_MEDIA_TYPES+ array.
-
#
-
# A request body is also assumed to contain form-data when no
-
# Content-Type header is provided and the request_method is POST.
-
1
def form_data?
-
type = media_type
-
meth = env["rack.methodoverride.original_method"] || env['REQUEST_METHOD']
-
(meth == 'POST' && type.nil?) || FORM_DATA_MEDIA_TYPES.include?(type)
-
end
-
-
# Determine whether the request body contains data by checking
-
# the request media_type against registered parse-data media-types
-
1
def parseable_data?
-
PARSEABLE_DATA_MEDIA_TYPES.include?(media_type)
-
end
-
-
# Returns the data received in the query string.
-
1
def GET
-
1
if @env["rack.request.query_string"] == query_string
-
@env["rack.request.query_hash"]
-
else
-
1
@env["rack.request.query_string"] = query_string
-
1
@env["rack.request.query_hash"] = parse_query(query_string)
-
end
-
end
-
-
# Returns the data received in the request body.
-
#
-
# This method support both application/x-www-form-urlencoded and
-
# multipart/form-data.
-
1
def POST
-
1
if @env["rack.input"].nil?
-
raise "Missing rack.input"
-
1
elsif @env["rack.request.form_input"].eql? @env["rack.input"]
-
@env["rack.request.form_hash"]
-
1
elsif form_data? || parseable_data?
-
1
@env["rack.request.form_input"] = @env["rack.input"]
-
1
unless @env["rack.request.form_hash"] = parse_multipart(env)
-
1
form_vars = @env["rack.input"].read
-
-
# Fix for Safari Ajax postings that always append \0
-
# form_vars.sub!(/\0\z/, '') # performance replacement:
-
1
form_vars.slice!(-1) if form_vars[-1] == ?\0
-
-
1
@env["rack.request.form_vars"] = form_vars
-
1
@env["rack.request.form_hash"] = parse_query(form_vars)
-
-
1
@env["rack.input"].rewind
-
end
-
1
@env["rack.request.form_hash"]
-
else
-
{}
-
end
-
end
-
-
# The union of GET and POST data.
-
1
def params
-
@params ||= self.GET.merge(self.POST)
-
rescue EOFError
-
self.GET
-
end
-
-
# shortcut for request.params[key]
-
1
def [](key)
-
params[key.to_s]
-
end
-
-
# shortcut for request.params[key] = value
-
1
def []=(key, value)
-
params[key.to_s] = value
-
end
-
-
# like Hash#values_at
-
1
def values_at(*keys)
-
keys.map{|key| params[key] }
-
end
-
-
# the referer of the client
-
1
def referer
-
@env['HTTP_REFERER']
-
end
-
1
alias referrer referer
-
-
1
def user_agent
-
@env['HTTP_USER_AGENT']
-
end
-
-
1
def cookies
-
hash = @env["rack.request.cookie_hash"] ||= {}
-
string = @env["HTTP_COOKIE"]
-
-
return hash if string == @env["rack.request.cookie_string"]
-
hash.clear
-
-
# According to RFC 2109:
-
# If multiple cookies satisfy the criteria above, they are ordered in
-
# the Cookie header such that those with more specific Path attributes
-
# precede those with less specific. Ordering with respect to other
-
# attributes (e.g., Domain) is unspecified.
-
Utils.parse_query(string, ';,').each { |k,v| hash[k] = Array === v ? v.first : v }
-
@env["rack.request.cookie_string"] = string
-
hash
-
rescue => error
-
error.message.replace "cannot parse Cookie header: #{error.message}"
-
raise
-
end
-
-
1
def xhr?
-
@env["HTTP_X_REQUESTED_WITH"] == "XMLHttpRequest"
-
end
-
-
1
def base_url
-
url = scheme + "://"
-
url << host
-
-
if scheme == "https" && port != 443 ||
-
scheme == "http" && port != 80
-
url << ":#{port}"
-
end
-
-
url
-
end
-
-
# Tries to return a remake of the original request URL as a string.
-
1
def url
-
base_url + fullpath
-
end
-
-
1
def path
-
1
script_name + path_info
-
end
-
-
1
def fullpath
-
1
query_string.empty? ? path : "#{path}?#{query_string}"
-
end
-
-
1
def accept_encoding
-
@env["HTTP_ACCEPT_ENCODING"].to_s.split(/\s*,\s*/).map do |part|
-
encoding, parameters = part.split(/\s*;\s*/, 2)
-
quality = 1.0
-
if parameters and /\Aq=([\d.]+)/ =~ parameters
-
quality = $1.to_f
-
end
-
[encoding, quality]
-
end
-
end
-
-
1
def trusted_proxy?(ip)
-
1
ip =~ /^127\.0\.0\.1$|^(10|172\.(1[6-9]|2[0-9]|30|31)|192\.168)\.|^::1$|^fd[0-9a-f]{2}:.+|^localhost$/i
-
end
-
-
1
def ip
-
1
remote_addrs = @env['REMOTE_ADDR'] ? @env['REMOTE_ADDR'].split(/[,\s]+/) : []
-
2
remote_addrs.reject! { |addr| trusted_proxy?(addr) }
-
-
1
return remote_addrs.first if remote_addrs.any?
-
-
1
forwarded_ips = @env['HTTP_X_FORWARDED_FOR'] ? @env['HTTP_X_FORWARDED_FOR'].strip.split(/[,\s]+/) : []
-
-
1
if client_ip = @env['HTTP_CLIENT_IP']
-
# If forwarded_ips doesn't include the client_ip, it might be an
-
# ip spoofing attempt, so we ignore HTTP_CLIENT_IP
-
return client_ip if forwarded_ips.include?(client_ip)
-
end
-
-
1
return forwarded_ips.reject { |ip| trusted_proxy?(ip) }.last || @env["REMOTE_ADDR"]
-
end
-
-
1
protected
-
1
def parse_query(qs)
-
2
Utils.parse_nested_query(qs)
-
end
-
-
1
def parse_multipart(env)
-
1
Rack::Multipart.parse_multipart(env)
-
end
-
end
-
end
-
1
require 'rack/request'
-
1
require 'rack/utils'
-
1
require 'time'
-
-
1
module Rack
-
# Rack::Response provides a convenient interface to create a Rack
-
# response.
-
#
-
# It allows setting of headers and cookies, and provides useful
-
# defaults (a OK response containing HTML).
-
#
-
# You can use Response#write to iteratively generate your response,
-
# but note that this is buffered by Rack::Response until you call
-
# +finish+. +finish+ however can take a block inside which calls to
-
# +write+ are synchronous with the Rack response.
-
#
-
# Your application's +call+ should end returning Response#finish.
-
-
1
class Response
-
1
attr_accessor :length
-
-
1
def initialize(body=[], status=200, header={})
-
1
@status = status.to_i
-
1
@header = Utils::HeaderHash.new("Content-Type" => "text/html").
-
merge(header)
-
-
1
@chunked = "chunked" == @header['Transfer-Encoding']
-
2
@writer = lambda { |x| @body << x }
-
1
@block = nil
-
1
@length = 0
-
-
1
@body = []
-
-
1
if body.respond_to? :to_str
-
write body.to_str
-
elsif body.respond_to?(:each)
-
1
body.each { |part|
-
1
write part.to_s
-
}
-
else
-
raise TypeError, "stringable or iterable required"
-
end
-
-
1
yield self if block_given?
-
end
-
-
1
attr_reader :header
-
1
attr_accessor :status, :body
-
-
1
def [](key)
-
header[key]
-
end
-
-
1
def []=(key, value)
-
header[key] = value
-
end
-
-
1
def set_cookie(key, value)
-
Utils.set_cookie_header!(header, key, value)
-
end
-
-
1
def delete_cookie(key, value={})
-
Utils.delete_cookie_header!(header, key, value)
-
end
-
-
1
def redirect(target, status=302)
-
self.status = status
-
self["Location"] = target
-
end
-
-
1
def finish(&block)
-
1
@block = block
-
-
1
if [204, 205, 304].include?(status.to_i)
-
header.delete "Content-Type"
-
header.delete "Content-Length"
-
[status.to_i, header, []]
-
else
-
1
[status.to_i, header, self]
-
end
-
end
-
1
alias to_a finish # For *response
-
1
alias to_ary finish # For implicit-splat on Ruby 1.9.2
-
-
1
def each(&callback)
-
@body.each(&callback)
-
@writer = callback
-
@block.call(self) if @block
-
end
-
-
# Append to body and update Content-Length.
-
#
-
# NOTE: Do not mix #write and direct #body access!
-
#
-
1
def write(str)
-
1
s = str.to_s
-
1
@length += Rack::Utils.bytesize(s) unless @chunked
-
1
@writer.call s
-
-
1
header["Content-Length"] = @length.to_s unless @chunked
-
1
str
-
end
-
-
1
def close
-
body.close if body.respond_to?(:close)
-
end
-
-
1
def empty?
-
@block == nil && @body.empty?
-
end
-
-
1
alias headers header
-
-
1
module Helpers
-
1
def invalid?; status < 100 || status >= 600; end
-
-
1
def informational?; status >= 100 && status < 200; end
-
1
def successful?; status >= 200 && status < 300; end
-
1
def redirection?; status >= 300 && status < 400; end
-
1
def client_error?; status >= 400 && status < 500; end
-
1
def server_error?; status >= 500 && status < 600; end
-
-
1
def ok?; status == 200; end
-
1
def bad_request?; status == 400; end
-
1
def forbidden?; status == 403; end
-
1
def not_found?; status == 404; end
-
1
def method_not_allowed?; status == 405; end
-
1
def unprocessable?; status == 422; end
-
-
1
def redirect?; [301, 302, 303, 307].include? status; end
-
-
# Headers
-
1
attr_reader :headers, :original_headers
-
-
1
def include?(header)
-
!!headers[header]
-
end
-
-
1
def content_type
-
headers["Content-Type"]
-
end
-
-
1
def content_length
-
cl = headers["Content-Length"]
-
cl ? cl.to_i : cl
-
end
-
-
1
def location
-
headers["Location"]
-
end
-
end
-
-
1
include Helpers
-
end
-
end
-
# AUTHOR: blink <blinketje@gmail.com>; blink#ruby-lang@irc.freenode.net
-
# bugrep: Andreas Zehnder
-
-
1
require 'time'
-
1
require 'rack/request'
-
1
require 'rack/response'
-
1
begin
-
1
require 'securerandom'
-
rescue LoadError
-
# We just won't get securerandom
-
end
-
-
1
module Rack
-
-
1
module Session
-
-
1
module Abstract
-
1
ENV_SESSION_KEY = 'rack.session'.freeze
-
1
ENV_SESSION_OPTIONS_KEY = 'rack.session.options'.freeze
-
-
# Thin wrapper around Hash that allows us to lazily load session id into session_options.
-
-
1
class OptionsHash < Hash #:nodoc:
-
1
def initialize(by, env, default_options)
-
@by = by
-
@env = env
-
@session_id_loaded = false
-
merge!(default_options)
-
end
-
-
1
def [](key)
-
load_session_id! if key == :id && session_id_not_loaded?
-
super
-
end
-
-
1
private
-
-
1
def session_id_not_loaded?
-
!(@session_id_loaded || key?(:id))
-
end
-
-
1
def load_session_id!
-
self[:id] = @by.send(:extract_session_id, @env)
-
@session_id_loaded = true
-
end
-
end
-
-
# SessionHash is responsible to lazily load the session from store.
-
-
1
class SessionHash < Hash
-
1
def initialize(by, env)
-
super()
-
@by = by
-
@env = env
-
@loaded = false
-
end
-
-
1
def [](key)
-
load_for_read!
-
super(key.to_s)
-
end
-
-
1
def has_key?(key)
-
load_for_read!
-
super(key.to_s)
-
end
-
1
alias :key? :has_key?
-
1
alias :include? :has_key?
-
-
1
def []=(key, value)
-
load_for_write!
-
super(key.to_s, value)
-
end
-
-
1
def clear
-
load_for_write!
-
super
-
end
-
-
1
def to_hash
-
load_for_read!
-
h = {}.replace(self)
-
h.delete_if { |k,v| v.nil? }
-
h
-
end
-
-
1
def update(hash)
-
load_for_write!
-
super(stringify_keys(hash))
-
end
-
-
1
def delete(key)
-
load_for_write!
-
super(key.to_s)
-
end
-
-
1
def inspect
-
if loaded?
-
super
-
else
-
"#<#{self.class}:0x#{self.object_id.to_s(16)} not yet loaded>"
-
end
-
end
-
-
1
def exists?
-
return @exists if instance_variable_defined?(:@exists)
-
@exists = @by.send(:session_exists?, @env)
-
end
-
-
1
def loaded?
-
@loaded
-
end
-
-
1
def empty?
-
load_for_read!
-
super
-
end
-
-
1
private
-
-
1
def load_for_read!
-
load! if !loaded? && exists?
-
end
-
-
1
def load_for_write!
-
load! unless loaded?
-
end
-
-
1
def load!
-
id, session = @by.send(:load_session, @env)
-
@env[ENV_SESSION_OPTIONS_KEY][:id] = id
-
replace(stringify_keys(session))
-
@loaded = true
-
end
-
-
1
def stringify_keys(other)
-
hash = {}
-
other.each do |key, value|
-
hash[key.to_s] = value
-
end
-
hash
-
end
-
end
-
-
# ID sets up a basic framework for implementing an id based sessioning
-
# service. Cookies sent to the client for maintaining sessions will only
-
# contain an id reference. Only #get_session and #set_session are
-
# required to be overwritten.
-
#
-
# All parameters are optional.
-
# * :key determines the name of the cookie, by default it is
-
# 'rack.session'
-
# * :path, :domain, :expire_after, :secure, and :httponly set the related
-
# cookie options as by Rack::Response#add_cookie
-
# * :skip will not a set a cookie in the response nor update the session state
-
# * :defer will not set a cookie in the response but still update the session
-
# state if it is used with a backend
-
# * :renew (implementation dependent) will prompt the generation of a new
-
# session id, and migration of data to be referenced at the new id. If
-
# :defer is set, it will be overridden and the cookie will be set.
-
# * :sidbits sets the number of bits in length that a generated session
-
# id will be.
-
#
-
# These options can be set on a per request basis, at the location of
-
# env['rack.session.options']. Additionally the id of the session can be
-
# found within the options hash at the key :id. It is highly not
-
# recommended to change its value.
-
#
-
# Is Rack::Utils::Context compatible.
-
#
-
# Not included by default; you must require 'rack/session/abstract/id'
-
# to use.
-
-
1
class ID
-
1
DEFAULT_OPTIONS = {
-
:key => 'rack.session',
-
:path => '/',
-
:domain => nil,
-
:expire_after => nil,
-
:secure => false,
-
:httponly => true,
-
:defer => false,
-
:renew => false,
-
:sidbits => 128,
-
:cookie_only => true,
-
1
:secure_random => (::SecureRandom rescue false)
-
}
-
-
1
attr_reader :key, :default_options
-
-
1
def initialize(app, options={})
-
@app = app
-
@default_options = self.class::DEFAULT_OPTIONS.merge(options)
-
@key = @default_options.delete(:key)
-
@cookie_only = @default_options.delete(:cookie_only)
-
initialize_sid
-
end
-
-
1
def call(env)
-
context(env)
-
end
-
-
1
def context(env, app=@app)
-
prepare_session(env)
-
status, headers, body = app.call(env)
-
commit_session(env, status, headers, body)
-
end
-
-
1
private
-
-
1
def initialize_sid
-
@sidbits = @default_options[:sidbits]
-
@sid_secure = @default_options[:secure_random]
-
@sid_length = @sidbits / 4
-
end
-
-
# Generate a new session id using Ruby #rand. The size of the
-
# session id is controlled by the :sidbits option.
-
# Monkey patch this to use custom methods for session id generation.
-
-
1
def generate_sid(secure = @sid_secure)
-
if secure
-
SecureRandom.hex(@sid_length)
-
else
-
"%0#{@sid_length}x" % Kernel.rand(2**@sidbits - 1)
-
end
-
rescue NotImplementedError
-
generate_sid(false)
-
end
-
-
# Sets the lazy session at 'rack.session' and places options and session
-
# metadata into 'rack.session.options'.
-
-
1
def prepare_session(env)
-
session_was = env[ENV_SESSION_KEY]
-
env[ENV_SESSION_KEY] = SessionHash.new(self, env)
-
env[ENV_SESSION_OPTIONS_KEY] = OptionsHash.new(self, env, @default_options)
-
env[ENV_SESSION_KEY].merge! session_was if session_was
-
end
-
-
# Extracts the session id from provided cookies and passes it and the
-
# environment to #get_session.
-
-
1
def load_session(env)
-
sid = current_session_id(env)
-
sid, session = get_session(env, sid)
-
[sid, session || {}]
-
end
-
-
# Extract session id from request object.
-
-
1
def extract_session_id(env)
-
request = Rack::Request.new(env)
-
sid = request.cookies[@key]
-
sid ||= request.params[@key] unless @cookie_only
-
sid
-
end
-
-
# Returns the current session id from the OptionsHash.
-
-
1
def current_session_id(env)
-
env[ENV_SESSION_OPTIONS_KEY][:id]
-
end
-
-
# Check if the session exists or not.
-
-
1
def session_exists?(env)
-
value = current_session_id(env)
-
value && !value.empty?
-
end
-
-
# Session should be commited if it was loaded, any of specific options like :renew, :drop
-
# or :expire_after was given and the security permissions match. Skips if skip is given.
-
-
1
def commit_session?(env, session, options)
-
if options[:skip]
-
false
-
else
-
has_session = loaded_session?(session) || forced_session_update?(session, options)
-
has_session && security_matches?(env, options)
-
end
-
end
-
-
1
def loaded_session?(session)
-
!session.is_a?(SessionHash) || session.loaded?
-
end
-
-
1
def forced_session_update?(session, options)
-
force_options?(options) && session && !session.empty?
-
end
-
-
1
def force_options?(options)
-
options.values_at(:renew, :drop, :defer, :expire_after).any?
-
end
-
-
1
def security_matches?(env, options)
-
return true unless options[:secure]
-
request = Rack::Request.new(env)
-
request.ssl?
-
end
-
-
# Acquires the session from the environment and the session id from
-
# the session options and passes them to #set_session. If successful
-
# and the :defer option is not true, a cookie will be added to the
-
# response with the session's id.
-
-
1
def commit_session(env, status, headers, body)
-
session = env['rack.session']
-
options = env['rack.session.options']
-
-
if options[:drop] || options[:renew]
-
session_id = destroy_session(env, options[:id] || generate_sid, options)
-
return [status, headers, body] unless session_id
-
end
-
-
return [status, headers, body] unless commit_session?(env, session, options)
-
-
session.send(:load!) unless loaded_session?(session)
-
session = session.to_hash
-
session_id ||= options[:id] || generate_sid
-
-
if not data = set_session(env, session_id, session, options)
-
env["rack.errors"].puts("Warning! #{self.class.name} failed to save session. Content dropped.")
-
elsif options[:defer] and not options[:renew]
-
env["rack.errors"].puts("Defering cookie for #{session_id}") if $VERBOSE
-
else
-
cookie = Hash.new
-
cookie[:value] = data
-
cookie[:expires] = Time.now + options[:expire_after] if options[:expire_after]
-
set_cookie(env, headers, cookie.merge!(options))
-
end
-
-
[status, headers, body]
-
end
-
-
# Sets the cookie back to the client with session id. We skip the cookie
-
# setting if the value didn't change (sid is the same) or expires was given.
-
-
1
def set_cookie(env, headers, cookie)
-
request = Rack::Request.new(env)
-
if request.cookies[@key] != cookie[:value] || cookie[:expires]
-
Utils.set_cookie_header!(headers, @key, cookie)
-
end
-
end
-
-
# All thread safety and session retrival proceedures should occur here.
-
# Should return [session_id, session].
-
# If nil is provided as the session id, generation of a new valid id
-
# should occur within.
-
-
1
def get_session(env, sid)
-
raise '#get_session not implemented.'
-
end
-
-
# All thread safety and session storage proceedures should occur here.
-
# Should return true or false dependant on whether or not the session
-
# was saved or not.
-
-
1
def set_session(env, sid, session, options)
-
raise '#set_session not implemented.'
-
end
-
-
# All thread safety and session destroy proceedures should occur here.
-
# Should return a new session id or nil if options[:drop]
-
-
1
def destroy_session(env, sid, options)
-
raise '#destroy_session not implemented'
-
end
-
end
-
end
-
end
-
end
-
# -*- encoding: binary -*-
-
1
require 'fileutils'
-
1
require 'set'
-
1
require 'tempfile'
-
1
require 'rack/multipart'
-
-
4
major, minor, patch = RUBY_VERSION.split('.').map { |v| v.to_i }
-
-
1
if major == 1 && minor < 9
-
require 'rack/backports/uri/common_18'
-
elsif major == 1 && minor == 9 && patch < 3
-
require 'rack/backports/uri/common_192'
-
else
-
1
require 'uri/common'
-
end
-
-
1
module Rack
-
# Rack::Utils contains a grab-bag of useful methods for writing web
-
# applications adopted from all kinds of Ruby libraries.
-
-
1
module Utils
-
# URI escapes. (CGI style space to +)
-
1
def escape(s)
-
URI.encode_www_form_component(s)
-
end
-
1
module_function :escape
-
-
# Like URI escaping, but with %20 instead of +. Strictly speaking this is
-
# true URI escaping.
-
1
def escape_path(s)
-
escape(s).gsub('+', '%20')
-
end
-
1
module_function :escape_path
-
-
# Unescapes a URI escaped string with +encoding+. +encoding+ will be the
-
# target encoding of the string returned, and it defaults to UTF-8
-
1
if defined?(::Encoding)
-
1
def unescape(s, encoding = Encoding::UTF_8)
-
URI.decode_www_form_component(s, encoding)
-
end
-
else
-
def unescape(s, encoding = nil)
-
URI.decode_www_form_component(s, encoding)
-
end
-
end
-
1
module_function :unescape
-
-
1
DEFAULT_SEP = /[&;] */n
-
-
1
class << self
-
1
attr_accessor :key_space_limit
-
end
-
-
# The default number of bytes to allow parameter keys to take up.
-
# This helps prevent a rogue client from flooding a Request.
-
1
self.key_space_limit = 65536
-
-
# Stolen from Mongrel, with some small modifications:
-
# Parses a query string by breaking it up at the '&'
-
# and ';' characters. You can also use this to parse
-
# cookies by changing the characters used in the second
-
# parameter (which defaults to '&;').
-
1
def parse_query(qs, d = nil)
-
params = KeySpaceConstrainedParams.new
-
-
(qs || '').split(d ? /[#{d}] */n : DEFAULT_SEP).each do |p|
-
k, v = p.split('=', 2).map { |x| unescape(x) }
-
-
if cur = params[k]
-
if cur.class == Array
-
params[k] << v
-
else
-
params[k] = [cur, v]
-
end
-
else
-
params[k] = v
-
end
-
end
-
-
return params.to_params_hash
-
end
-
1
module_function :parse_query
-
-
1
def parse_nested_query(qs, d = nil)
-
2
params = KeySpaceConstrainedParams.new
-
-
2
(qs || '').split(d ? /[#{d}] */n : DEFAULT_SEP).each do |p|
-
k, v = p.split('=', 2).map { |s| unescape(s) }
-
-
normalize_params(params, k, v)
-
end
-
-
2
return params.to_params_hash
-
end
-
1
module_function :parse_nested_query
-
-
1
def normalize_params(params, name, v = nil)
-
name =~ %r(\A[\[\]]*([^\[\]]+)\]*)
-
k = $1 || ''
-
after = $' || ''
-
-
return if k.empty?
-
-
if after == ""
-
params[k] = v
-
elsif after == "[]"
-
params[k] ||= []
-
raise TypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array)
-
params[k] << v
-
elsif after =~ %r(^\[\]\[([^\[\]]+)\]$) || after =~ %r(^\[\](.+)$)
-
child_key = $1
-
params[k] ||= []
-
raise TypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array)
-
if params_hash_type?(params[k].last) && !params[k].last.key?(child_key)
-
normalize_params(params[k].last, child_key, v)
-
else
-
params[k] << normalize_params(params.class.new, child_key, v)
-
end
-
else
-
params[k] ||= params.class.new
-
raise TypeError, "expected Hash (got #{params[k].class.name}) for param `#{k}'" unless params_hash_type?(params[k])
-
params[k] = normalize_params(params[k], after, v)
-
end
-
-
return params
-
end
-
1
module_function :normalize_params
-
-
1
def params_hash_type?(obj)
-
obj.kind_of?(KeySpaceConstrainedParams) || obj.kind_of?(Hash)
-
end
-
1
module_function :params_hash_type?
-
-
1
def build_query(params)
-
params.map { |k, v|
-
if v.class == Array
-
build_query(v.map { |x| [k, x] })
-
else
-
v.nil? ? escape(k) : "#{escape(k)}=#{escape(v)}"
-
end
-
}.join("&")
-
end
-
1
module_function :build_query
-
-
1
def build_nested_query(value, prefix = nil)
-
case value
-
when Array
-
value.map { |v|
-
build_nested_query(v, "#{prefix}[]")
-
}.join("&")
-
when Hash
-
value.map { |k, v|
-
build_nested_query(v, prefix ? "#{prefix}[#{escape(k)}]" : escape(k))
-
}.join("&")
-
when String
-
raise ArgumentError, "value must be a Hash" if prefix.nil?
-
"#{prefix}=#{escape(value)}"
-
else
-
prefix
-
end
-
end
-
1
module_function :build_nested_query
-
-
1
ESCAPE_HTML = {
-
"&" => "&",
-
"<" => "<",
-
">" => ">",
-
"'" => "'",
-
'"' => """,
-
"/" => "/"
-
}
-
1
if //.respond_to?(:encoding)
-
1
ESCAPE_HTML_PATTERN = Regexp.union(*ESCAPE_HTML.keys)
-
else
-
# On 1.8, there is a kcode = 'u' bug that allows for XSS otherwhise
-
# TODO doesn't apply to jruby, so a better condition above might be preferable?
-
ESCAPE_HTML_PATTERN = /#{Regexp.union(*ESCAPE_HTML.keys)}/n
-
end
-
-
# Escape ampersands, brackets and quotes to their HTML/XML entities.
-
1
def escape_html(string)
-
string.to_s.gsub(ESCAPE_HTML_PATTERN){|c| ESCAPE_HTML[c] }
-
end
-
1
module_function :escape_html
-
-
1
def select_best_encoding(available_encodings, accept_encoding)
-
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
-
-
expanded_accept_encoding =
-
accept_encoding.map { |m, q|
-
if m == "*"
-
(available_encodings - accept_encoding.map { |m2, _| m2 }).map { |m2| [m2, q] }
-
else
-
[[m, q]]
-
end
-
}.inject([]) { |mem, list|
-
mem + list
-
}
-
-
encoding_candidates = expanded_accept_encoding.sort_by { |_, q| -q }.map { |m, _| m }
-
-
unless encoding_candidates.include?("identity")
-
encoding_candidates.push("identity")
-
end
-
-
expanded_accept_encoding.find_all { |m, q|
-
q == 0.0
-
}.each { |m, _|
-
encoding_candidates.delete(m)
-
}
-
-
return (encoding_candidates & available_encodings)[0]
-
end
-
1
module_function :select_best_encoding
-
-
1
def set_cookie_header!(header, key, value)
-
case value
-
when Hash
-
domain = "; domain=" + value[:domain] if value[:domain]
-
path = "; path=" + value[:path] if value[:path]
-
# According to RFC 2109, we need dashes here.
-
# N.B.: cgi.rb uses spaces...
-
expires = "; expires=" +
-
rfc2822(value[:expires].clone.gmtime) if value[:expires]
-
secure = "; secure" if value[:secure]
-
httponly = "; HttpOnly" if value[:httponly]
-
value = value[:value]
-
end
-
value = [value] unless Array === value
-
cookie = escape(key) + "=" +
-
value.map { |v| escape v }.join("&") +
-
"#{domain}#{path}#{expires}#{secure}#{httponly}"
-
-
case header["Set-Cookie"]
-
when nil, ''
-
header["Set-Cookie"] = cookie
-
when String
-
header["Set-Cookie"] = [header["Set-Cookie"], cookie].join("\n")
-
when Array
-
header["Set-Cookie"] = (header["Set-Cookie"] + [cookie]).join("\n")
-
end
-
-
nil
-
end
-
1
module_function :set_cookie_header!
-
-
1
def delete_cookie_header!(header, key, value = {})
-
case header["Set-Cookie"]
-
when nil, ''
-
cookies = []
-
when String
-
cookies = header["Set-Cookie"].split("\n")
-
when Array
-
cookies = header["Set-Cookie"]
-
end
-
-
cookies.reject! { |cookie|
-
if value[:domain]
-
cookie =~ /\A#{escape(key)}=.*domain=#{value[:domain]}/
-
elsif value[:path]
-
cookie =~ /\A#{escape(key)}=.*path=#{value[:path]}/
-
else
-
cookie =~ /\A#{escape(key)}=/
-
end
-
}
-
-
header["Set-Cookie"] = cookies.join("\n")
-
-
set_cookie_header!(header, key,
-
{:value => '', :path => nil, :domain => nil,
-
:expires => Time.at(0) }.merge(value))
-
-
nil
-
end
-
1
module_function :delete_cookie_header!
-
-
# Return the bytesize of String; uses String#size under Ruby 1.8 and
-
# String#bytesize under 1.9.
-
1
if ''.respond_to?(:bytesize)
-
1
def bytesize(string)
-
1
string.bytesize
-
end
-
else
-
def bytesize(string)
-
string.size
-
end
-
end
-
1
module_function :bytesize
-
-
# Modified version of stdlib time.rb Time#rfc2822 to use '%d-%b-%Y' instead
-
# of '% %b %Y'.
-
# It assumes that the time is in GMT to comply to the RFC 2109.
-
#
-
# NOTE: I'm not sure the RFC says it requires GMT, but is ambigous enough
-
# that I'm certain someone implemented only that option.
-
# Do not use %a and %b from Time.strptime, it would use localized names for
-
# weekday and month.
-
#
-
1
def rfc2822(time)
-
wday = Time::RFC2822_DAY_NAME[time.wday]
-
mon = Time::RFC2822_MONTH_NAME[time.mon - 1]
-
time.strftime("#{wday}, %d-#{mon}-%Y %H:%M:%S GMT")
-
end
-
1
module_function :rfc2822
-
-
# Parses the "Range:" header, if present, into an array of Range objects.
-
# Returns nil if the header is missing or syntactically invalid.
-
# Returns an empty array if none of the ranges are satisfiable.
-
1
def byte_ranges(env, size)
-
# See <http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35>
-
http_range = env['HTTP_RANGE']
-
return nil unless http_range
-
ranges = []
-
http_range.split(/,\s*/).each do |range_spec|
-
matches = range_spec.match(/bytes=(\d*)-(\d*)/)
-
return nil unless matches
-
r0,r1 = matches[1], matches[2]
-
if r0.empty?
-
return nil if r1.empty?
-
# suffix-byte-range-spec, represents trailing suffix of file
-
r0 = [size - r1.to_i, 0].max
-
r1 = size - 1
-
else
-
r0 = r0.to_i
-
if r1.empty?
-
r1 = size - 1
-
else
-
r1 = r1.to_i
-
return nil if r1 < r0 # backwards range is syntactically invalid
-
r1 = size-1 if r1 >= size
-
end
-
end
-
ranges << (r0..r1) if r0 <= r1
-
end
-
ranges
-
end
-
1
module_function :byte_ranges
-
-
# Context allows the use of a compatible middleware at different points
-
# in a request handling stack. A compatible middleware must define
-
# #context which should take the arguments env and app. The first of which
-
# would be the request environment. The second of which would be the rack
-
# application that the request would be forwarded to.
-
1
class Context
-
1
attr_reader :for, :app
-
-
1
def initialize(app_f, app_r)
-
raise 'running context does not respond to #context' unless app_f.respond_to? :context
-
@for, @app = app_f, app_r
-
end
-
-
1
def call(env)
-
@for.context(env, @app)
-
end
-
-
1
def recontext(app)
-
self.class.new(@for, app)
-
end
-
-
1
def context(env, app=@app)
-
recontext(app).call(env)
-
end
-
end
-
-
# A case-insensitive Hash that preserves the original case of a
-
# header when set.
-
1
class HeaderHash < Hash
-
1
def self.new(hash={})
-
1
HeaderHash === hash ? hash : super(hash)
-
end
-
-
1
def initialize(hash={})
-
1
super()
-
1
@names = {}
-
2
hash.each { |k, v| self[k] = v }
-
end
-
-
1
def each
-
super do |k, v|
-
yield(k, v.respond_to?(:to_ary) ? v.to_ary.join("\n") : v)
-
end
-
end
-
-
1
def to_hash
-
hash = {}
-
each { |k,v| hash[k] = v }
-
hash
-
end
-
-
1
def [](k)
-
5
super(k) || super(@names[k.downcase])
-
end
-
-
1
def []=(k, v)
-
3
canonical = k.downcase
-
3
delete k if @names[canonical] && @names[canonical] != k # .delete is expensive, don't invoke it unless necessary
-
3
@names[k] = @names[canonical] = k
-
3
super k, v
-
end
-
-
1
def delete(k)
-
canonical = k.downcase
-
result = super @names.delete(canonical)
-
@names.delete_if { |name,| name.downcase == canonical }
-
result
-
end
-
-
1
def include?(k)
-
@names.include?(k) || @names.include?(k.downcase)
-
end
-
-
1
alias_method :has_key?, :include?
-
1
alias_method :member?, :include?
-
1
alias_method :key?, :include?
-
-
1
def merge!(other)
-
2
other.each { |k, v| self[k] = v }
-
1
self
-
end
-
-
1
def merge(other)
-
1
hash = dup
-
1
hash.merge! other
-
end
-
-
1
def replace(other)
-
clear
-
other.each { |k, v| self[k] = v }
-
self
-
end
-
end
-
-
1
class KeySpaceConstrainedParams
-
1
def initialize(limit = Utils.key_space_limit)
-
2
@limit = limit
-
2
@size = 0
-
2
@params = {}
-
end
-
-
1
def [](key)
-
@params[key]
-
end
-
-
1
def []=(key, value)
-
@size += key.size unless @params.key?(key)
-
raise RangeError, 'exceeded available parameter key space' if @size > @limit
-
@params[key] = value
-
end
-
-
1
def key?(key)
-
@params.key?(key)
-
end
-
-
1
def to_params_hash
-
2
hash = @params
-
2
hash.keys.each do |key|
-
value = hash[key]
-
if value.kind_of?(self.class)
-
hash[key] = value.to_params_hash
-
elsif value.kind_of?(Array)
-
value.map! {|x| x.kind_of?(self.class) ? x.to_params_hash : x}
-
end
-
end
-
2
hash
-
end
-
end
-
-
# Every standard HTTP code mapped to the appropriate message.
-
# Generated with:
-
# curl -s http://www.iana.org/assignments/http-status-codes | \
-
# ruby -ane 'm = /^(\d{3}) +(\S[^\[(]+)/.match($_) and
-
# puts " #{m[1]} => \x27#{m[2].strip}x27,"'
-
1
HTTP_STATUS_CODES = {
-
100 => 'Continue',
-
101 => 'Switching Protocols',
-
102 => 'Processing',
-
200 => 'OK',
-
201 => 'Created',
-
202 => 'Accepted',
-
203 => 'Non-Authoritative Information',
-
204 => 'No Content',
-
205 => 'Reset Content',
-
206 => 'Partial Content',
-
207 => 'Multi-Status',
-
226 => 'IM Used',
-
300 => 'Multiple Choices',
-
301 => 'Moved Permanently',
-
302 => 'Found',
-
303 => 'See Other',
-
304 => 'Not Modified',
-
305 => 'Use Proxy',
-
306 => 'Reserved',
-
307 => 'Temporary Redirect',
-
400 => 'Bad Request',
-
401 => 'Unauthorized',
-
402 => 'Payment Required',
-
403 => 'Forbidden',
-
404 => 'Not Found',
-
405 => 'Method Not Allowed',
-
406 => 'Not Acceptable',
-
407 => 'Proxy Authentication Required',
-
408 => 'Request Timeout',
-
409 => 'Conflict',
-
410 => 'Gone',
-
411 => 'Length Required',
-
412 => 'Precondition Failed',
-
413 => 'Request Entity Too Large',
-
414 => 'Request-URI Too Long',
-
415 => 'Unsupported Media Type',
-
416 => 'Requested Range Not Satisfiable',
-
417 => 'Expectation Failed',
-
418 => "I'm a Teapot",
-
422 => 'Unprocessable Entity',
-
423 => 'Locked',
-
424 => 'Failed Dependency',
-
426 => 'Upgrade Required',
-
500 => 'Internal Server Error',
-
501 => 'Not Implemented',
-
502 => 'Bad Gateway',
-
503 => 'Service Unavailable',
-
504 => 'Gateway Timeout',
-
505 => 'HTTP Version Not Supported',
-
506 => 'Variant Also Negotiates',
-
507 => 'Insufficient Storage',
-
510 => 'Not Extended',
-
}
-
-
# Responses with HTTP status codes that should not have an entity body
-
1
STATUS_WITH_NO_ENTITY_BODY = Set.new((100..199).to_a << 204 << 205 << 304)
-
-
1
SYMBOL_TO_STATUS_CODE = Hash[*HTTP_STATUS_CODES.map { |code, message|
-
52
[message.downcase.gsub(/\s|-/, '_').to_sym, code]
-
}.flatten]
-
-
1
def status_code(status)
-
2
if status.is_a?(Symbol)
-
SYMBOL_TO_STATUS_CODE[status] || 500
-
else
-
2
status.to_i
-
end
-
end
-
1
module_function :status_code
-
-
1
Multipart = Rack::Multipart
-
-
end
-
end
-
# Define a package task library to aid in the definition of
-
# redistributable package files.
-
-
1
require 'rake'
-
1
require 'rake/tasklib'
-
-
1
module Rake
-
-
# Create a packaging task that will package the project into
-
# distributable files (e.g zip archive or tar files).
-
#
-
# The PackageTask will create the following targets:
-
#
-
# [<b>:package</b>]
-
# Create all the requested package files.
-
#
-
# [<b>:clobber_package</b>]
-
# Delete all the package files. This target is automatically
-
# added to the main clobber target.
-
#
-
# [<b>:repackage</b>]
-
# Rebuild the package files from scratch, even if they are not out
-
# of date.
-
#
-
# [<b>"<em>package_dir</em>/<em>name</em>-<em>version</em>.tgz"</b>]
-
# Create a gzipped tar package (if <em>need_tar</em> is true).
-
#
-
# [<b>"<em>package_dir</em>/<em>name</em>-<em>version</em>.tar.gz"</b>]
-
# Create a gzipped tar package (if <em>need_tar_gz</em> is true).
-
#
-
# [<b>"<em>package_dir</em>/<em>name</em>-<em>version</em>.tar.bz2"</b>]
-
# Create a bzip2'd tar package (if <em>need_tar_bz2</em> is true).
-
#
-
# [<b>"<em>package_dir</em>/<em>name</em>-<em>version</em>.zip"</b>]
-
# Create a zip package archive (if <em>need_zip</em> is true).
-
#
-
# Example:
-
#
-
# Rake::PackageTask.new("rake", "1.2.3") do |p|
-
# p.need_tar = true
-
# p.package_files.include("lib/**/*.rb")
-
# end
-
#
-
1
class PackageTask < TaskLib
-
# Name of the package (from the GEM Spec).
-
1
attr_accessor :name
-
-
# Version of the package (e.g. '1.3.2').
-
1
attr_accessor :version
-
-
# Directory used to store the package files (default is 'pkg').
-
1
attr_accessor :package_dir
-
-
# True if a gzipped tar file (tgz) should be produced (default is false).
-
1
attr_accessor :need_tar
-
-
# True if a gzipped tar file (tar.gz) should be produced (default is false).
-
1
attr_accessor :need_tar_gz
-
-
# True if a bzip2'd tar file (tar.bz2) should be produced (default is false).
-
1
attr_accessor :need_tar_bz2
-
-
# True if a zip file should be produced (default is false)
-
1
attr_accessor :need_zip
-
-
# List of files to be included in the package.
-
1
attr_accessor :package_files
-
-
# Tar command for gzipped or bzip2ed archives. The default is 'tar'.
-
1
attr_accessor :tar_command
-
-
# Zip command for zipped archives. The default is 'zip'.
-
1
attr_accessor :zip_command
-
-
# Create a Package Task with the given name and version. Use +:noversion+
-
# as the version to build a package without a version or to provide a
-
# fully-versioned package name.
-
-
1
def initialize(name=nil, version=nil)
-
init(name, version)
-
yield self if block_given?
-
define unless name.nil?
-
end
-
-
# Initialization that bypasses the "yield self" and "define" step.
-
1
def init(name, version)
-
1
@name = name
-
1
@version = version
-
1
@package_files = Rake::FileList.new
-
1
@package_dir = 'pkg'
-
1
@need_tar = false
-
1
@need_tar_gz = false
-
1
@need_tar_bz2 = false
-
1
@need_zip = false
-
1
@tar_command = 'tar'
-
1
@zip_command = 'zip'
-
end
-
-
# Create the tasks defined by this task library.
-
1
def define
-
1
fail "Version required (or :noversion)" if @version.nil?
-
1
@version = nil if :noversion == @version
-
-
1
desc "Build all the packages"
-
1
task :package
-
-
1
desc "Force a rebuild of the package files"
-
1
task :repackage => [:clobber_package, :package]
-
-
1
desc "Remove package products"
-
1
task :clobber_package do
-
rm_r package_dir rescue nil
-
end
-
-
1
task :clobber => [:clobber_package]
-
-
[
-
1
[need_tar, tgz_file, "z"],
-
[need_tar_gz, tar_gz_file, "z"],
-
[need_tar_bz2, tar_bz2_file, "j"]
-
].each do |(need, file, flag)|
-
3
if need
-
task :package => ["#{package_dir}/#{file}"]
-
file "#{package_dir}/#{file}" => [package_dir_path] + package_files do
-
chdir(package_dir) do
-
sh %{#{@tar_command} #{flag}cvf #{file} #{package_name}}
-
end
-
end
-
end
-
end
-
-
1
if need_zip
-
task :package => ["#{package_dir}/#{zip_file}"]
-
file "#{package_dir}/#{zip_file}" => [package_dir_path] + package_files do
-
chdir(package_dir) do
-
sh %{#{@zip_command} -r #{zip_file} #{package_name}}
-
end
-
end
-
end
-
-
1
directory package_dir
-
-
1
file package_dir_path => @package_files do
-
mkdir_p package_dir rescue nil
-
@package_files.each do |fn|
-
f = File.join(package_dir_path, fn)
-
fdir = File.dirname(f)
-
mkdir_p(fdir) if !File.exist?(fdir)
-
if File.directory?(fn)
-
mkdir_p(f)
-
else
-
rm_f f
-
safe_ln(fn, f)
-
end
-
end
-
end
-
1
self
-
end
-
-
1
def package_name
-
4
@version ? "#{@name}-#{@version}" : @name
-
end
-
-
1
def package_dir_path
-
1
"#{package_dir}/#{package_name}"
-
end
-
-
1
def tgz_file
-
1
"#{package_name}.tgz"
-
end
-
-
1
def tar_gz_file
-
1
"#{package_name}.tar.gz"
-
end
-
-
1
def tar_bz2_file
-
1
"#{package_name}.tar.bz2"
-
end
-
-
1
def zip_file
-
"#{package_name}.zip"
-
end
-
end
-
-
end
-
1
require 'rake'
-
-
1
module Rake
-
-
# Base class for Task Libraries.
-
1
class TaskLib
-
1
include Cloneable
-
1
include Rake::DSL
-
-
# Make a symbol by pasting two strings together.
-
#
-
# NOTE: DEPRECATED! This method is kinda stupid. I don't know why
-
# I didn't just use string interpolation. But now other task
-
# libraries depend on this so I can't remove it without breaking
-
# other people's code. So for now it stays for backwards
-
# compatibility. BUT DON'T USE IT.
-
1
def paste(a,b) # :nodoc:
-
(a.to_s + b.to_s).intern
-
end
-
end
-
-
end
-
1
require 'treetop/ruby_extensions/string'
-
1
class String
-
1
def column_of(index)
-
2
return 1 if index == 0
-
newline_index = rindex("\n", index - 1)
-
if newline_index
-
index - newline_index
-
else
-
index + 1
-
end
-
end
-
-
1
def line_of(index)
-
2
self[0...index].count("\n") + 1
-
end
-
-
1
unless method_defined?(:blank?)
-
def blank?
-
self == ""
-
end
-
end
-
-
# The following methods are lifted from Facets 2.0.2
-
1
def tabto(n)
-
if self =~ /^( *)\S/
-
indent(n - $1.length)
-
else
-
self
-
end
-
end
-
-
1
def indent(n)
-
if n >= 0
-
gsub(/^/, ' ' * n)
-
else
-
gsub(/^ {0,#{-n}}/, "")
-
end
-
end
-
-
1
def treetop_camelize
-
to_s.gsub(/\/(.?)/){ "::" + $1.upcase }.gsub(/(^|_)(.)/){ $2.upcase }
-
end
-
end
-
1
require 'treetop/ruby_extensions'
-
-
1
require 'treetop/runtime/compiled_parser'
-
1
require 'treetop/runtime/syntax_node'
-
1
require 'treetop/runtime/terminal_parse_failure'
-
1
require 'treetop/runtime/interval_skip_list'
-
1
module Treetop
-
1
module Runtime
-
1
class CompiledParser
-
1
include Treetop::Runtime
-
-
1
attr_reader :input, :index, :max_terminal_failure_index
-
1
attr_writer :root
-
1
attr_accessor :consume_all_input
-
1
alias :consume_all_input? :consume_all_input
-
-
1
def initialize
-
1004
self.consume_all_input = true
-
end
-
-
1
def parse(input, options = {})
-
1004
prepare_to_parse(input)
-
1004
@index = options[:index] if options[:index]
-
1004
result = send("_nt_#{options[:root] || root}")
-
1004
should_consume_all = options.include?(:consume_all_input) ? options[:consume_all_input] : consume_all_input?
-
1004
return nil if (should_consume_all && index != input.size)
-
1002
return SyntaxNode.new(input, index...(index + 1)) if result == true
-
1002
return result
-
end
-
-
1
def failure_index
-
8
max_terminal_failure_index
-
end
-
-
1
def failure_line
-
2
@terminal_failures && input.line_of(failure_index)
-
end
-
-
1
def failure_column
-
2
@terminal_failures && input.column_of(failure_index)
-
end
-
-
1
def failure_reason
-
2
return nil unless (tf = terminal_failures) && tf.size > 0
-
"Expected " +
-
2
(tf.size == 1 ?
-
tf[0].expected_string :
-
6
"one of #{tf.map{|f| f.expected_string}.uniq*', '}"
-
) +
-
" at line #{failure_line}, column #{failure_column} (byte #{failure_index+1})" +
-
2
" after #{input[index...failure_index]}"
-
end
-
-
1
def terminal_failures
-
2
if @terminal_failures.empty? || @terminal_failures[0].is_a?(TerminalParseFailure)
-
@terminal_failures
-
else
-
8
@terminal_failures.map! {|tf_ary| TerminalParseFailure.new(*tf_ary) }
-
end
-
end
-
-
-
1
protected
-
-
1
attr_reader :node_cache, :input_length
-
1
attr_writer :index
-
-
1
def prepare_to_parse(input)
-
1004
@input = input
-
1004
@input_length = input.length
-
1004
reset_index
-
20862
@node_cache = Hash.new {|hash, key| hash[key] = Hash.new}
-
1004
@regexps = {}
-
1004
@terminal_failures = []
-
1004
@max_terminal_failure_index = 0
-
end
-
-
1
def reset_index
-
1004
@index = 0
-
end
-
-
1
def parse_anything(node_class = SyntaxNode, inline_module = nil)
-
if index < input.length
-
result = instantiate_node(node_class,input, index...(index + 1))
-
result.extend(inline_module) if inline_module
-
@index += 1
-
result
-
else
-
terminal_parse_failure("any character")
-
end
-
end
-
-
1
def instantiate_node(node_type,*args)
-
47368
if node_type.respond_to? :new
-
47368
node_type.new(*args)
-
else
-
SyntaxNode.new(*args).extend(node_type)
-
end
-
end
-
-
1
def has_terminal?(terminal, regex, index)
-
99627
if regex
-
52391
rx = @regexps[terminal] ||= Regexp.new(terminal)
-
52391
input.index(rx, index) == index
-
else
-
47236
input[index, terminal.size] == terminal
-
end
-
end
-
-
1
def terminal_parse_failure(expected_string)
-
44208
return nil if index < max_terminal_failure_index
-
41820
if index > max_terminal_failure_index
-
5710
@max_terminal_failure_index = index
-
5710
@terminal_failures = []
-
end
-
41820
@terminal_failures << [index, expected_string]
-
return nil
-
end
-
end
-
end
-
end
-
1
require 'treetop/runtime/interval_skip_list/interval_skip_list'
-
1
require 'treetop/runtime/interval_skip_list/head_node'
-
1
require 'treetop/runtime/interval_skip_list/node'
-
1
class IntervalSkipList
-
1
class HeadNode
-
1
attr_reader :height, :forward, :forward_markers
-
-
1
def initialize(height)
-
@height = height
-
@forward = Array.new(height, nil)
-
@forward_markers = Array.new(height) {|i| []}
-
end
-
-
1
def top_level
-
height - 1
-
end
-
end
-
end
-
1
class IntervalSkipList
-
1
attr_reader :probability
-
-
1
def initialize
-
@head = HeadNode.new(max_height)
-
@ranges = {}
-
@probability = 0.5
-
end
-
-
1
def max_height
-
3
-
end
-
-
1
def empty?
-
head.forward[0].nil?
-
end
-
-
1
def expire(range, length_change)
-
expired_markers, first_node_after_range = overlapping(range)
-
expired_markers.each { |marker| delete(marker) }
-
first_node_after_range.propagate_length_change(length_change)
-
end
-
-
1
def overlapping(range)
-
markers, first_node = containing_with_node(range.first)
-
-
cur_node = first_node
-
begin
-
markers.concat(cur_node.forward_markers.flatten)
-
cur_node = cur_node.forward[0]
-
end while cur_node.key < range.last
-
-
return markers.uniq, cur_node
-
end
-
-
1
def containing(n)
-
containing_with_node(n).first
-
end
-
-
1
def insert(range, marker)
-
ranges[marker] = range
-
first_node = insert_node(range.first)
-
first_node.endpoint_of.push(marker)
-
last_node = insert_node(range.last)
-
last_node.endpoint_of.push(marker)
-
-
cur_node = first_node
-
cur_level = first_node.top_level
-
while next_node_at_level_inside_range?(cur_node, cur_level, range)
-
while can_ascend_from?(cur_node, cur_level) && next_node_at_level_inside_range?(cur_node, cur_level + 1, range)
-
cur_level += 1
-
end
-
cur_node = mark_forward_path_at_level(cur_node, cur_level, marker)
-
end
-
-
while node_inside_range?(cur_node, range)
-
while can_descend_from?(cur_level) && next_node_at_level_outside_range?(cur_node, cur_level, range)
-
cur_level -= 1
-
end
-
cur_node = mark_forward_path_at_level(cur_node, cur_level, marker)
-
end
-
end
-
-
1
def delete(marker)
-
range = ranges[marker]
-
path_to_first_node = make_path
-
first_node = find(range.first, path_to_first_node)
-
-
cur_node = first_node
-
cur_level = first_node.top_level
-
while next_node_at_level_inside_range?(cur_node, cur_level, range)
-
while can_ascend_from?(cur_node, cur_level) && next_node_at_level_inside_range?(cur_node, cur_level + 1, range)
-
cur_level += 1
-
end
-
cur_node = unmark_forward_path_at_level(cur_node, cur_level, marker)
-
end
-
-
while node_inside_range?(cur_node, range)
-
while can_descend_from?(cur_level) && next_node_at_level_outside_range?(cur_node, cur_level, range)
-
cur_level -= 1
-
end
-
cur_node = unmark_forward_path_at_level(cur_node, cur_level, marker)
-
end
-
last_node = cur_node
-
-
first_node.endpoint_of.delete(marker)
-
if first_node.endpoint_of.empty?
-
first_node.delete(path_to_first_node)
-
end
-
-
last_node.endpoint_of.delete(marker)
-
if last_node.endpoint_of.empty?
-
path_to_last_node = make_path
-
find(range.last, path_to_last_node)
-
last_node.delete(path_to_last_node)
-
end
-
end
-
-
1
protected
-
1
attr_reader :head, :ranges
-
-
1
def insert_node(key)
-
path = make_path
-
found_node = find(key, path)
-
if found_node && found_node.key == key
-
return found_node
-
else
-
return Node.new(key, next_node_height, path)
-
end
-
end
-
-
1
def containing_with_node(n)
-
containing = []
-
cur_node = head
-
(max_height - 1).downto(0) do |cur_level|
-
while (next_node = cur_node.forward[cur_level]) && next_node.key <= n
-
cur_node = next_node
-
if cur_node.key == n
-
return containing + (cur_node.markers - cur_node.endpoint_of), cur_node
-
end
-
end
-
containing.concat(cur_node.forward_markers[cur_level])
-
end
-
-
return containing, cur_node
-
end
-
-
1
def delete_node(key)
-
path = make_path
-
found_node = find(key, path)
-
found_node.delete(path) if found_node.key == key
-
end
-
-
1
def find(key, path)
-
cur_node = head
-
(max_height - 1).downto(0) do |cur_level|
-
while (next_node = cur_node.forward[cur_level]) && next_node.key < key
-
cur_node = next_node
-
end
-
path[cur_level] = cur_node
-
end
-
cur_node.forward[0]
-
end
-
-
1
def make_path
-
Array.new(max_height, nil)
-
end
-
-
1
def next_node_height
-
height = 1
-
while rand < probability && height < max_height
-
height += 1
-
end
-
height
-
end
-
-
1
def can_ascend_from?(node, level)
-
level < node.top_level
-
end
-
-
1
def can_descend_from?(level)
-
level > 0
-
end
-
-
1
def node_inside_range?(node, range)
-
node.key < range.last
-
end
-
-
1
def next_node_at_level_inside_range?(node, level, range)
-
node.forward[level] && node.forward[level].key <= range.last
-
end
-
-
1
def next_node_at_level_outside_range?(node, level, range)
-
(node.forward[level].nil? || node.forward[level].key > range.last)
-
end
-
-
1
def mark_forward_path_at_level(node, level, marker)
-
node.forward_markers[level].push(marker)
-
next_node = node.forward[level]
-
next_node.markers.push(marker)
-
node = next_node
-
end
-
-
1
def unmark_forward_path_at_level(node, level, marker)
-
node.forward_markers[level].delete(marker)
-
next_node = node.forward[level]
-
next_node.markers.delete(marker)
-
node = next_node
-
end
-
-
1
def nodes
-
nodes = []
-
cur_node = head.forward[0]
-
until cur_node.nil?
-
nodes << cur_node
-
cur_node = cur_node.forward[0]
-
end
-
nodes
-
end
-
end
-
1
class IntervalSkipList
-
1
class Node < HeadNode
-
1
attr_accessor :key
-
1
attr_reader :markers, :endpoint_of
-
-
1
def initialize(key, height, path)
-
super(height)
-
@key = key
-
@markers = []
-
@endpoint_of = []
-
update_forward_pointers(path)
-
promote_markers(path)
-
end
-
-
1
def all_forward_markers
-
markers.flatten
-
end
-
-
1
def delete(path)
-
0.upto(top_level) do |i|
-
path[i].forward[i] = forward[i]
-
end
-
demote_markers(path)
-
end
-
-
1
def propagate_length_change(length_change)
-
cur_node = self
-
while cur_node do
-
cur_node.key += length_change
-
cur_node = cur_node.forward[0]
-
end
-
end
-
-
1
protected
-
-
1
def update_forward_pointers(path)
-
0.upto(top_level) do |i|
-
forward[i] = path[i].forward[i]
-
path[i].forward[i] = self
-
end
-
end
-
-
1
def promote_markers(path)
-
promoted = []
-
new_promoted = []
-
0.upto(top_level) do |i|
-
incoming_markers = path[i].forward_markers[i]
-
markers.concat(incoming_markers)
-
-
incoming_markers.each do |marker|
-
if can_be_promoted_higher?(marker, i)
-
new_promoted.push(marker)
-
forward[i].delete_marker_from_path(marker, i, forward[i+1])
-
else
-
forward_markers[i].push(marker)
-
end
-
end
-
-
promoted.each do |marker|
-
if can_be_promoted_higher?(marker, i)
-
new_promoted.push(marker)
-
forward[i].delete_marker_from_path(marker, i, forward[i+1])
-
else
-
forward_markers[i].push(marker)
-
end
-
end
-
-
promoted = new_promoted
-
new_promoted = []
-
end
-
end
-
-
-
1
def can_be_promoted_higher?(marker, level)
-
level < top_level && forward[level + 1] && forward[level + 1].markers.include?(marker)
-
end
-
-
1
def delete_marker_from_path(marker, level, terminus)
-
cur_node = self
-
until cur_node == terminus
-
cur_node.forward_markers[level].delete(marker)
-
cur_node.markers.delete(marker)
-
cur_node = cur_node.forward[level]
-
end
-
end
-
-
1
def demote_markers(path)
-
demote_inbound_markers(path)
-
demote_outbound_markers(path)
-
end
-
-
1
def demote_inbound_markers(path)
-
demoted = []
-
new_demoted = []
-
-
top_level.downto(0) do |i|
-
incoming_markers = path[i].forward_markers[i].dup
-
incoming_markers.each do |marker|
-
unless forward_node_with_marker_at_or_above_level?(marker, i)
-
path[i].forward_markers[i].delete(marker)
-
new_demoted.push(marker)
-
end
-
end
-
-
demoted.each do |marker|
-
path[i + 1].place_marker_on_inbound_path(marker, i, path[i])
-
-
if forward[i].markers.include?(marker)
-
path[i].forward_markers[i].push(marker)
-
else
-
new_demoted.push(marker)
-
end
-
end
-
-
demoted = new_demoted
-
new_demoted = []
-
end
-
end
-
-
1
def demote_outbound_markers(path)
-
demoted = []
-
new_demoted = []
-
-
top_level.downto(0) do |i|
-
forward_markers[i].each do |marker|
-
new_demoted.push(marker) unless path[i].forward_markers[i].include?(marker)
-
end
-
-
demoted.each do |marker|
-
forward[i].place_marker_on_outbound_path(marker, i, forward[i + 1])
-
new_demoted.push(marker) unless path[i].forward_markers[i].include?(marker)
-
end
-
-
demoted = new_demoted
-
new_demoted = []
-
end
-
end
-
-
1
def forward_node_with_marker_at_or_above_level?(marker, level)
-
level.upto(top_level) do |i|
-
return true if forward[i].markers.include?(marker)
-
end
-
false
-
end
-
-
1
def place_marker_on_outbound_path(marker, level, terminus)
-
cur_node = self
-
until cur_node == terminus
-
cur_node.forward_markers[level].push(marker)
-
cur_node.markers.push(marker)
-
cur_node = cur_node.forward[level]
-
end
-
end
-
-
1
def place_marker_on_inbound_path(marker, level, terminus)
-
cur_node = self
-
until cur_node == terminus
-
cur_node.forward_markers[level].push(marker)
-
cur_node = cur_node.forward[level]
-
cur_node.markers.push(marker)
-
end
-
end
-
end
-
end
-
1
module Treetop
-
1
module Runtime
-
1
class TerminalParseFailure
-
1
attr_reader :index, :expected_string
-
-
1
def initialize(index, expected_string)
-
6
@index = index
-
6
@expected_string = expected_string
-
end
-
-
1
def to_s
-
"String matching #{expected_string} expected."
-
end
-
end
-
end
-
end